]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/simpleinit.c
Imported from util-linux-2.2 tarball.
[thirdparty/util-linux.git] / login-utils / simpleinit.c
1 /* simpleinit.c - poe@daimi.aau.dk */
2 /* Version 1.21 */
3
4 #include <sys/types.h>
5 #include <stdlib.h>
6 #include <unistd.h>
7 #include <limits.h>
8 #include <stdio.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <signal.h>
12 #include <pwd.h>
13 #include <sys/file.h>
14 #include <sys/wait.h>
15 #include <sys/stat.h>
16 #include <sys/sysmacros.h>
17 #include <utmp.h>
18 #ifdef SHADOW_PWD
19 #include <shadow.h>
20 #endif
21
22 #include "pathnames.h"
23
24 #define CMDSIZ 150 /* max size of a line in inittab */
25 #define NUMCMD 30 /* max number of lines in inittab */
26 #define NUMTOK 20 /* max number of tokens in inittab command */
27
28 #define RUN_RC
29 #define TZFILE "/etc/TZ"
30 char tzone[CMDSIZ];
31 /* #define DEBUGGING */
32
33 /* Define this if you want init to ignore the termcap field in inittab for
34 console ttys. */
35 /* #define SPECIAL_CONSOLE_TERM */
36
37 #define ever (;;)
38
39 struct initline {
40 pid_t pid;
41 char tty[10];
42 char termcap[30];
43 char *toks[NUMTOK];
44 char line[CMDSIZ];
45 };
46
47 struct initline inittab[NUMCMD];
48 int numcmd;
49 int stopped = 0; /* are we stopped */
50
51 int do_rc();
52 void spawn(), hup_handler(), read_inittab();
53 void tstp_handler(), int_handler(), set_tz(), write_wtmp();
54 int boot_single();
55
56 void err(char *s)
57 {
58 int fd;
59
60 if((fd = open("/dev/console", O_WRONLY)) < 0) return;
61
62 write(fd, "init: ", 6);
63 write(fd, s, strlen(s));
64 close(fd);
65 }
66
67 void
68 enter_single()
69 {
70 pid_t pid;
71 int i;
72
73 err("Booting to single user mode.\n");
74 if((pid = fork()) == 0) {
75 /* the child */
76 execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
77 err("exec of single user shell failed\n");
78 } else if(pid > 0) {
79 while(wait(&i) != pid) /* nothing */;
80 } else if(pid < 0) {
81 err("fork of single user shell failed\n");
82 }
83 unlink(_PATH_SINGLE);
84 }
85
86 int main(int argc, char *argv[])
87 {
88 int vec,i;
89 pid_t pid;
90
91 #ifdef SET_TZ
92 set_tz();
93 #endif
94 signal(SIGTSTP, tstp_handler);
95 signal(SIGINT, int_handler);
96
97 /*
98 * start up in single user mode if /etc/singleboot exists or if
99 * argv[1] is "single".
100 */
101 if(boot_single(0, argc, argv)) enter_single();
102
103 #ifdef RUN_RC
104 /*If we get a SIGTSTP before multi-user mode, do nothing*/
105 while(stopped)
106 pause();
107 if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped)
108 enter_single();
109 while(stopped) /*Also if /etc/rc fails & we get SIGTSTP*/
110 pause();
111 #endif
112
113 write_wtmp(); /* write boottime record */
114
115 for(i = 0; i < NUMCMD; i++)
116 inittab[i].pid = -1;
117
118 read_inittab();
119
120 #ifdef DEBUGGING
121 for(i = 0; i < numcmd; i++) {
122 char **p;
123 p = inittab[i].toks;
124 printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
125 printf("tty= %s\n", inittab[i].tty);
126 printf("termcap= %s\n", inittab[i].termcap);
127 }
128 exit(0);
129 #endif
130 signal(SIGHUP, hup_handler);
131
132 for(i = 0; i < getdtablesize(); i++) close(i);
133
134 for(i = 0; i < numcmd; i++)
135 spawn(i);
136
137 for ever {
138 pid = wait(&vec);
139
140 /* clear utmp entry, and append to wtmp if possible */
141 {
142 struct utmp *ut;
143 int ut_fd;
144
145 utmpname(_PATH_UTMP);
146 setutent();
147 while(ut = getutent()) {
148 if(ut->ut_pid == pid) {
149 time(&ut->ut_time);
150 memset(&ut->ut_user, 0, UT_NAMESIZE);
151 memset(&ut->ut_host, 0, sizeof(ut->ut_host));
152 ut->ut_type = DEAD_PROCESS;
153 ut->ut_pid = 0;
154 ut->ut_addr = 0;
155 endutent();
156 pututline(ut);
157 if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
158 flock(ut_fd, LOCK_EX|LOCK_NB);
159 write(ut_fd, ut, sizeof(struct utmp));
160 flock(ut_fd, LOCK_UN|LOCK_NB);
161 close(ut_fd);
162 }
163 break;
164 }
165 }
166 endutent();
167 }
168
169 for(i = 0; i < numcmd; i++) {
170 if(pid == inittab[i].pid || inittab[i].pid < 0) {
171 if(stopped) inittab[i].pid = -1;
172 else spawn(i);
173 break;
174 }
175 }
176 }
177 }
178
179 #define MAXTRIES 3 /* number of tries allowed when giving the password */
180
181 /*
182 * return true if we should boot up in singleuser mode. If argv[i] is
183 * "single" or the file /etc/singleboot exists, then singleuser mode should
184 * be entered. If /etc/securesingle exists ask for root password first.
185 */
186 int boot_single(int singlearg, int argc, char *argv[])
187 {
188 char *pass, *rootpass = NULL;
189 struct passwd *pwd;
190 int i;
191
192 for(i = 1; i < argc; i++) {
193 if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1;
194 }
195
196 if(access(_PATH_SINGLE, 04) == 0 || singlearg) {
197 if(access(_PATH_SECURE, 04) == 0) {
198 if((pwd = getpwnam("root")) || (pwd = getpwuid(0)))
199 rootpass = pwd->pw_passwd;
200 else
201 return 1; /* a bad /etc/passwd should not lock out */
202
203 for(i = 0; i < MAXTRIES; i++) {
204 pass = getpass("Password: ");
205 if(pass == NULL) continue;
206
207 if(!strcmp(crypt(pass, rootpass), rootpass)) {
208 return 1;
209 }
210
211 puts("\nWrong password.\n");
212 }
213 } else return 1;
214 }
215 return 0;
216 }
217
218 /*
219 * run /etc/rc. The environment is passed to the script, so the RC environment
220 * variable can be used to decide what to do. RC may be set from LILO.
221 */
222 int do_rc()
223 {
224 pid_t pid;
225 int stat;
226
227 if((pid = fork()) == 0) {
228 /* the child */
229 char *argv[2];
230
231 argv[0] = _PATH_BSHELL;
232 argv[1] = (char *)0;
233
234 close(0);
235 if(open(_PATH_RC, O_RDONLY, 0) == 0) {
236 execv(_PATH_BSHELL, argv);
237 err("exec rc failed\n");
238 _exit(2);
239 }
240 err("open of rc file failed\n");
241 _exit(1);
242 } else if(pid > 0) {
243 /* parent, wait till rc process dies before spawning */
244 while(wait(&stat) != pid) /* nothing */;
245 } else if(pid < 0) {
246 err("fork of rc shell failed\n");
247 }
248 return WEXITSTATUS(stat);
249 }
250
251 void spawn(int i)
252 {
253 pid_t pid;
254 int j;
255
256 if((pid = fork()) < 0) {
257 inittab[i].pid = -1;
258 err("fork failed\n");
259 return;
260 }
261 if(pid) {
262 /* this is the parent */
263 inittab[i].pid = pid;
264 return;
265 } else {
266 /* this is the child */
267 char term[40];
268 char tz[CMDSIZ];
269 char *env[3];
270
271 setsid();
272 for(j = 0; j < getdtablesize(); j++)
273 (void) close(j);
274
275 (void) sprintf(term, "TERM=%s", inittab[i].termcap);
276 env[0] = term;
277 env[1] = (char *)0;
278 #ifdef SET_TZ
279 (void) sprintf(tz, "TZ=%s", tzone);
280 env[1] = tz;
281 #endif
282 env[2] = (char *)0;
283
284 execve(inittab[i].toks[0], inittab[i].toks, env);
285 err("exec failed\n");
286 sleep(5);
287 _exit(1);
288 }
289 }
290
291 void read_inittab()
292 {
293 FILE *f;
294 char buf[CMDSIZ];
295 int i,j,k;
296 char *ptr, *getty;
297 char tty[50];
298 struct stat stb;
299 char *termenv, *getenv();
300
301 termenv = getenv("TERM"); /* set by kernel */
302 /* termenv = "vt100"; */
303
304 if(!(f = fopen(_PATH_INITTAB, "r"))) {
305 err("cannot open inittab\n");
306 _exit(1);
307 }
308
309 i = 0;
310 while(!feof(f) && i < NUMCMD - 2) {
311 if(fgets(buf, CMDSIZ - 1, f) == 0) break;
312 buf[CMDSIZ-1] = 0;
313
314 for(k = 0; k < CMDSIZ && buf[k]; k++) {
315 if(buf[k] == '#') {
316 buf[k] = 0; break;
317 }
318 }
319
320 if(buf[0] == 0 || buf[0] == '\n') continue;
321
322 (void) strcpy(inittab[i].line, buf);
323
324 (void) strtok(inittab[i].line, ":");
325 (void) strncpy(inittab[i].tty, inittab[i].line, 10);
326 inittab[i].tty[9] = 0;
327 (void) strncpy(inittab[i].termcap,
328 strtok((char *)0, ":"), 30);
329 inittab[i].termcap[29] = 0;
330
331 getty = strtok((char *)0, ":");
332 (void) strtok(getty, " \t\n");
333 inittab[i].toks[0] = getty;
334 j = 1;
335 while(ptr = strtok((char *)0, " \t\n"))
336 inittab[i].toks[j++] = ptr;
337 inittab[i].toks[j] = (char *)0;
338
339 #ifdef SPECIAL_CONSOLE_TERM
340 /* special-case termcap for the console ttys */
341 (void) sprintf(tty, "/dev/%s", inittab[i].tty);
342 if(!termenv || stat(tty, &stb) < 0) {
343 err("no TERM or cannot stat tty\n");
344 } else {
345 /* is it a console tty? */
346 if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) {
347 strncpy(inittab[i].termcap, termenv, 30);
348 inittab[i].termcap[29] = 0;
349 }
350 }
351 #endif
352
353 i++;
354 }
355 fclose(f);
356 numcmd = i;
357 }
358
359 void hup_handler()
360 {
361 int i,j;
362 int oldnum;
363 struct initline savetab[NUMCMD];
364 int had_already;
365
366 (void) signal(SIGHUP, SIG_IGN);
367
368 memcpy(savetab, inittab, NUMCMD * sizeof(struct initline));
369 oldnum = numcmd;
370 read_inittab();
371
372 for(i = 0; i < numcmd; i++) {
373 had_already = 0;
374 for(j = 0; j < oldnum; j++) {
375 if(!strcmp(savetab[j].tty, inittab[i].tty)) {
376 had_already = 1;
377 if((inittab[i].pid = savetab[j].pid) < 0)
378 spawn(i);
379 }
380 }
381 if(!had_already) spawn(i);
382 }
383
384 (void) signal(SIGHUP, hup_handler);
385 }
386
387 void tstp_handler()
388 {
389 stopped = ~stopped;
390 if(!stopped) hup_handler();
391
392 signal(SIGTSTP, tstp_handler);
393 }
394
395 void int_handler()
396 {
397 /*
398 * After Linux 0.96b PL1, we get a SIGINT when
399 * the user presses Ctrl-Alt-Del...
400 */
401
402 int pid;
403
404 sync();
405 sync();
406 if((pid = fork()) == 0) {
407 /* reboot properly... */
408 execl(_PATH_REBOOT, _PATH_REBOOT, (char *)0);
409 reboot(0xfee1dead, 672274793, 0x1234567);
410 } else if(pid < 0)
411 /* fork failed, try the hard way... */
412 reboot(0xfee1dead, 672274793, 0x1234567);
413 }
414
415 void set_tz()
416 {
417 FILE *f;
418 int len;
419
420 if((f=fopen(TZFILE, "r")) == (FILE *)NULL) return;
421 fgets(tzone, CMDSIZ-2, f);
422 fclose(f);
423 if((len=strlen(tzone)) < 2) return;
424 tzone[len-1] = 0; /* get rid of the '\n' */
425 setenv("TZ", tzone, 0);
426 }
427
428 void write_wtmp()
429 {
430 int fd;
431 struct utmp ut;
432
433 memset((char *)&ut, 0, sizeof(ut));
434 strcpy(ut.ut_line, "~");
435 memset(ut.ut_name, 0, sizeof(ut.ut_name));
436 time(&ut.ut_time);
437 ut.ut_type = BOOT_TIME;
438
439 if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
440 flock(fd, LOCK_EX|LOCK_NB); /* make sure init won't hang */
441 write(fd, (char *)&ut, sizeof(ut));
442 flock(fd, LOCK_UN|LOCK_NB);
443 close(fd);
444 }
445 }