1 | /* $Id: dm.c,v 1.18 2000-11-08 23:17:02 ghudson Exp $ |
---|
2 | * |
---|
3 | * Copyright (c) 1990, 1991 by the Massachusetts Institute of Technology |
---|
4 | * For copying and distribution information, please see the file |
---|
5 | * <mit-copyright.h>. |
---|
6 | * |
---|
7 | * This is the top-level of the display manager and console control |
---|
8 | * for Athena's xlogin. |
---|
9 | */ |
---|
10 | |
---|
11 | #include <mit-copyright.h> |
---|
12 | |
---|
13 | #include <sys/types.h> |
---|
14 | #include <sys/ioctl.h> |
---|
15 | #include <sys/wait.h> |
---|
16 | #include <sys/file.h> |
---|
17 | #include <sys/stat.h> |
---|
18 | #include <sys/time.h> |
---|
19 | #ifdef HAVE_SYS_STRREDIR_H |
---|
20 | #include <sys/strredir.h> |
---|
21 | #endif |
---|
22 | #ifdef HAVE_SYS_STROPTS_H |
---|
23 | #include <sys/stropts.h> |
---|
24 | #endif |
---|
25 | #include <stdio.h> |
---|
26 | #include <stdlib.h> |
---|
27 | #include <unistd.h> |
---|
28 | #include <signal.h> |
---|
29 | #include <fcntl.h> |
---|
30 | #include <utmp.h> |
---|
31 | #include <ctype.h> |
---|
32 | #include <string.h> |
---|
33 | #include <errno.h> |
---|
34 | #ifdef HAVE_UTMPX_H |
---|
35 | #include <utmpx.h> |
---|
36 | #endif |
---|
37 | #include <termios.h> |
---|
38 | #include <syslog.h> |
---|
39 | #ifdef HAVE_UTIL_H |
---|
40 | #include <util.h> |
---|
41 | #endif |
---|
42 | #ifdef HAVE_PTY_H |
---|
43 | #include <pty.h> |
---|
44 | #endif |
---|
45 | |
---|
46 | #include <X11/Xlib.h> |
---|
47 | #include <al.h> |
---|
48 | |
---|
49 | #ifndef lint |
---|
50 | static const char rcsid[] = "$Id: dm.c,v 1.18 2000-11-08 23:17:02 ghudson Exp $"; |
---|
51 | #endif |
---|
52 | |
---|
53 | /* Process states */ |
---|
54 | #define NONEXISTENT 0 |
---|
55 | #define RUNNING 1 |
---|
56 | #define STARTUP 2 |
---|
57 | #define CONSOLELOGIN 3 |
---|
58 | #define FAILED 4 |
---|
59 | |
---|
60 | #ifndef FALSE |
---|
61 | #define FALSE 0 |
---|
62 | #define TRUE (!FALSE) |
---|
63 | #endif |
---|
64 | |
---|
65 | static sigset_t sig_zero; |
---|
66 | static sigset_t sig_cur; |
---|
67 | |
---|
68 | /* flags used by signal handlers */ |
---|
69 | pid_t xpid, consolepid, loginpid; |
---|
70 | volatile int alarm_running = NONEXISTENT; |
---|
71 | volatile int x_running = NONEXISTENT; |
---|
72 | volatile int console_running = NONEXISTENT; |
---|
73 | volatile int console_failed = FALSE; |
---|
74 | volatile int login_running = NONEXISTENT; |
---|
75 | char *logintty; |
---|
76 | int console_tty = 0; |
---|
77 | |
---|
78 | #if defined(UTMP_FILE) |
---|
79 | char *utmpf = UTMP_FILE; |
---|
80 | char *wtmpf = WTMP_FILE; |
---|
81 | #elif defined(_PATH_UTMP) |
---|
82 | char *utmpf = _PATH_UTMP; |
---|
83 | char *wtmpf = _PATH_WTMP; |
---|
84 | #else |
---|
85 | char *utmpf = "/var/adm/utmp"; |
---|
86 | char *wtmpf = "/var/adm/wtmp"; |
---|
87 | #endif |
---|
88 | char *xpids = "/var/athena/X%d.pid"; |
---|
89 | char *xhosts = "/etc/X%d.hosts"; |
---|
90 | char *consolepidf = "/var/athena/console.pid"; |
---|
91 | char *dmpidf = "/var/athena/dm.pid"; |
---|
92 | char *consolelog = "/var/athena/console.log"; |
---|
93 | |
---|
94 | static void die(int signo); |
---|
95 | static void child(int signo); |
---|
96 | static void catchalarm(int signo); |
---|
97 | static void xready(int signo); |
---|
98 | static void shutdown(int signo); |
---|
99 | static void loginready(int signo); |
---|
100 | static char *getconf(char *file, char *name); |
---|
101 | static char **parseargs(char *line, char *extra, char *extra1, char *extra2); |
---|
102 | static void console_login(char *conf, char *msg); |
---|
103 | static void start_console(int fd, char **argv, int redir); |
---|
104 | static void cleanup(char *tty); |
---|
105 | static pid_t fork_and_store(pid_t *var); |
---|
106 | static void x_stop_wait(void); |
---|
107 | static void writepid(char *file, pid_t pid); |
---|
108 | |
---|
109 | #ifndef HAVE_LOGOUT |
---|
110 | static void logout(const char *line); |
---|
111 | #endif |
---|
112 | #ifndef HAVE_LOGIN_TTY |
---|
113 | static int login_tty(int fd); |
---|
114 | #endif |
---|
115 | #ifndef HAVE_OPENPTY |
---|
116 | static int openpty(int *amaster, int *aslave, char *name, |
---|
117 | struct termios *termp, struct winsize *winp); |
---|
118 | static int termsetup(int fd, struct termios *termp, struct winsize *winp); |
---|
119 | #endif |
---|
120 | |
---|
121 | |
---|
122 | /* the console process will run as daemon */ |
---|
123 | #define DAEMON 1 |
---|
124 | |
---|
125 | #define X_START_WAIT 30 /* wait up to 30 seconds for X to be ready */ |
---|
126 | #define LOGIN_START_WAIT 60 /* wait up to 1 minute for Xlogin */ |
---|
127 | #ifndef BUFSIZ |
---|
128 | #define BUFSIZ 1024 |
---|
129 | #endif |
---|
130 | |
---|
131 | static int max_fd; |
---|
132 | |
---|
133 | /* Setup signals, start X, start console, start login, wait */ |
---|
134 | |
---|
135 | int main(int argc, char **argv) |
---|
136 | { |
---|
137 | char *consoletty, *conf, *p; |
---|
138 | char **dmargv, **xargv, **consoleargv = NULL, **loginargv; |
---|
139 | char xpidf[256], line[16], buf[256]; |
---|
140 | fd_set readfds; |
---|
141 | int pgrp, file, tries, count, redir = TRUE; |
---|
142 | char dpyname[10], dpyacl[40]; |
---|
143 | Display *dpy; |
---|
144 | XHostAddress *hosts; |
---|
145 | int nhosts, dpynum = 0; |
---|
146 | struct stat hostsinfo; |
---|
147 | Bool state; |
---|
148 | time_t now, last_console_failure = 0; |
---|
149 | struct sigaction sigact; |
---|
150 | sigset_t mask; |
---|
151 | #if defined(SRIOCSREDIR) || defined(TIOCCONS) |
---|
152 | int on; |
---|
153 | #endif |
---|
154 | int fd; |
---|
155 | char loginttyname[256]; |
---|
156 | int ttyfd; |
---|
157 | |
---|
158 | logintty = &loginttyname[5]; /* skip over the /dev/ */ |
---|
159 | |
---|
160 | sigemptyset(&sigact.sa_mask); |
---|
161 | sigact.sa_flags = 0; |
---|
162 | (void) sigemptyset(&sig_zero); |
---|
163 | |
---|
164 | /* |
---|
165 | * Note about setting environment variables in dm: |
---|
166 | * |
---|
167 | * All environment variables passed to dm and set in dm are |
---|
168 | * subsequently passed to any children of dm. This is usually |
---|
169 | * true of processes that exec in children, so that's not a |
---|
170 | * big surprise. |
---|
171 | * |
---|
172 | * However, xlogin is one of the children dm forks, and it goes |
---|
173 | * to lengths to ensure that the environments of users logging in |
---|
174 | * are ISOLATED from xlogin's own environment. Therefore, do not |
---|
175 | * expect that setting an environment variable here will reach the |
---|
176 | * user unless you have gone to lengths to make sure that xlogin |
---|
177 | * passes it on. Put another way, if you set a new environment |
---|
178 | * variable here, consider whether or not it should be seen by the |
---|
179 | * user. If it should, go modify verify.c as well. Consider also |
---|
180 | * whether the variable should be seen _only_ by the user. If so, |
---|
181 | * make the change only in xlogin, and not here. |
---|
182 | * |
---|
183 | * As an added complication, xlogin _does_ pass environment variables |
---|
184 | * on to the pre-login options. Therefore, if you set an environment |
---|
185 | * variable that should _not_ be seen, you must filter it in xlogin.c. |
---|
186 | * |
---|
187 | * Confused? Too bad. I'm in a nasty, if verbose, mood this year. |
---|
188 | * |
---|
189 | * General summary: |
---|
190 | * |
---|
191 | * If you add an environment variable here there are three likely |
---|
192 | * possibilities: |
---|
193 | * |
---|
194 | * 1. It's for the user only, not needed by any of dm's children. |
---|
195 | * --> Don't set it here. Set it in verify.c for users and in |
---|
196 | * --> xlogin.c for the pre-login options, if appropriate. |
---|
197 | * |
---|
198 | * 2. It's for dm and its children only, and _should not_ be seen |
---|
199 | * by the user or pre-login options. |
---|
200 | * --> You must filter the option from the pre-login options |
---|
201 | * --> in xlogin.c. No changes to verify.c are required. |
---|
202 | * |
---|
203 | * 3. It's for dm and the user and the pre-login options. |
---|
204 | * --> You must pass the option explicitly to the user in |
---|
205 | * --> verify.c. No changes to xlogin.c are required. |
---|
206 | * |
---|
207 | * --- cfields |
---|
208 | */ |
---|
209 | #ifdef notdef |
---|
210 | putenv("LD_LIBRARY_PATH=/usr/openwin/lib"); |
---|
211 | putenv("OPENWINHOME=/usr/openwin"); |
---|
212 | #endif |
---|
213 | |
---|
214 | if (argc < 2) |
---|
215 | { |
---|
216 | fprintf(stderr, "dm: first argument must be configuration file\n"); |
---|
217 | sleep(60); |
---|
218 | exit(1); |
---|
219 | } |
---|
220 | |
---|
221 | conf = argv[1]; |
---|
222 | |
---|
223 | if (argc != 4 && (argc != 5 || strcmp(argv[3], "-noconsole"))) |
---|
224 | { |
---|
225 | fprintf(stderr, |
---|
226 | "usage: %s configfile logintty [-noconsole] consoletty\n", |
---|
227 | argv[0]); |
---|
228 | console_login(conf, NULL); |
---|
229 | } |
---|
230 | if (argc == 5) |
---|
231 | redir = FALSE; |
---|
232 | |
---|
233 | /* parse argument lists */ |
---|
234 | /* ignore argv[2] */ |
---|
235 | consoletty = argv[argc - 1]; |
---|
236 | |
---|
237 | openlog("dm", 0, LOG_USER); |
---|
238 | |
---|
239 | /* We use options from the config file rather than taking |
---|
240 | * them from the command line because the current command |
---|
241 | * line form is gross (why???), and I don't see a good way |
---|
242 | * to extend it without making things grosser or breaking |
---|
243 | * backwards compatibility. So, we take a line from the |
---|
244 | * config file and use real parsing. |
---|
245 | */ |
---|
246 | p = getconf(conf, "dm"); |
---|
247 | if (p != NULL) |
---|
248 | { |
---|
249 | dmargv = parseargs(p, NULL, NULL, NULL); |
---|
250 | while (*dmargv) |
---|
251 | { |
---|
252 | if (!strcmp(*dmargv, "-display")) |
---|
253 | { |
---|
254 | dmargv++; |
---|
255 | if (*dmargv) |
---|
256 | { |
---|
257 | dpynum = atoi(*(dmargv) + 1); |
---|
258 | dmargv++; |
---|
259 | } |
---|
260 | } |
---|
261 | else |
---|
262 | dmargv++; |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | p = getconf(conf, "X"); |
---|
267 | if (p == NULL) |
---|
268 | console_login(conf, "\ndm: Can't find X command line\n"); |
---|
269 | xargv = parseargs(p, NULL, NULL, NULL); |
---|
270 | |
---|
271 | p = getconf(conf, "console"); |
---|
272 | if (p == NULL) |
---|
273 | console_login(conf, "\ndm: Can't find console command line\n"); |
---|
274 | |
---|
275 | consoleargv = parseargs(p, NULL, NULL, NULL); |
---|
276 | |
---|
277 | p = getconf(conf, "login"); |
---|
278 | if (p == NULL) |
---|
279 | console_login(conf, "\ndm: Can't find login command line\n"); |
---|
280 | loginargv = parseargs(p, logintty, "-tty", logintty); |
---|
281 | |
---|
282 | /* Signal Setup */ |
---|
283 | sigact.sa_handler = SIG_IGN; |
---|
284 | sigaction(SIGTSTP, &sigact, NULL); |
---|
285 | sigaction(SIGTTIN, &sigact, NULL); |
---|
286 | sigaction(SIGTTOU, &sigact, NULL); |
---|
287 | /* so that X pipe errors don't nuke us */ |
---|
288 | sigaction(SIGPIPE, &sigact, NULL); |
---|
289 | sigact.sa_handler = shutdown; |
---|
290 | sigaction(SIGFPE, &sigact, NULL); |
---|
291 | sigact.sa_handler = die; |
---|
292 | sigaction(SIGHUP, &sigact, NULL); |
---|
293 | sigaction(SIGINT, &sigact, NULL); |
---|
294 | sigaction(SIGTERM, &sigact, NULL); |
---|
295 | sigact.sa_handler = child; |
---|
296 | sigaction(SIGCHLD, &sigact, NULL); |
---|
297 | sigact.sa_handler = catchalarm; |
---|
298 | sigaction(SIGALRM, &sigact, NULL); |
---|
299 | |
---|
300 | strcpy(line, "/dev/"); |
---|
301 | strcat(line, consoletty); |
---|
302 | |
---|
303 | fd = open(line, O_RDWR); |
---|
304 | if (fd == -1) |
---|
305 | { |
---|
306 | syslog(LOG_ERR, "Cannot open %s: %m", line); |
---|
307 | /* This probably won't work, but it seems to be the appropriate |
---|
308 | punt location. */ |
---|
309 | console_login(conf, "Cannot open tty.\n"); |
---|
310 | } |
---|
311 | |
---|
312 | login_tty(fd); |
---|
313 | |
---|
314 | /* Set the console characteristics so we don't lose later */ |
---|
315 | setpgid(0, pgrp = getpid()); /* Reset the tty pgrp */ |
---|
316 | tcsetpgrp(0, pgrp); |
---|
317 | |
---|
318 | /* save our pid file */ |
---|
319 | writepid(dmpidf, getpid()); |
---|
320 | |
---|
321 | /* Fire up X */ |
---|
322 | xpid = 0; |
---|
323 | for (tries = 0; tries < 3; tries++) |
---|
324 | { |
---|
325 | syslog(LOG_DEBUG, "Starting X, try #%d", tries + 1); |
---|
326 | x_running = STARTUP; |
---|
327 | sigact.sa_handler = xready; |
---|
328 | sigaction(SIGUSR1, &sigact, NULL); |
---|
329 | switch (fork_and_store(&xpid)) |
---|
330 | { |
---|
331 | case 0: |
---|
332 | if (fcntl(2, F_SETFD, 1) == -1) |
---|
333 | close(2); |
---|
334 | (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); |
---|
335 | |
---|
336 | /* ignoring SIGUSR1 will cause the server to send us a SIGUSR1 |
---|
337 | * when it is ready to accept connections |
---|
338 | */ |
---|
339 | sigact.sa_handler = SIG_IGN; |
---|
340 | sigaction(SIGUSR1, &sigact, NULL); |
---|
341 | p = *xargv; |
---|
342 | *xargv = "X"; |
---|
343 | execv(p, xargv); |
---|
344 | fprintf(stderr, "dm: X server failed exec: %s\n", strerror(errno)); |
---|
345 | _exit(1); |
---|
346 | case -1: |
---|
347 | fprintf(stderr, "dm: Unable to fork to start X server: %s\n", |
---|
348 | strerror(errno)); |
---|
349 | break; |
---|
350 | default: |
---|
351 | sprintf(xpidf, xpids, dpynum); |
---|
352 | writepid(xpidf, xpid); |
---|
353 | |
---|
354 | if (x_running == STARTUP) |
---|
355 | { |
---|
356 | alarm(X_START_WAIT); |
---|
357 | alarm_running = RUNNING; |
---|
358 | sigsuspend(&sig_zero); |
---|
359 | } |
---|
360 | if (x_running != RUNNING) |
---|
361 | { |
---|
362 | syslog(LOG_DEBUG, "X failed to start; alarm_running=%d", |
---|
363 | alarm_running); |
---|
364 | if (alarm_running == NONEXISTENT) |
---|
365 | fprintf(stderr, "dm: Unable to start X\n"); |
---|
366 | else |
---|
367 | fprintf(stderr, "dm: X failed to become ready\n"); |
---|
368 | |
---|
369 | /* If X wouldn't run, it could be that an existing X |
---|
370 | * process hasn't shut down. Wait X_STOP_WAIT seconds |
---|
371 | * for that to happen. |
---|
372 | */ |
---|
373 | x_stop_wait(); |
---|
374 | } |
---|
375 | sigact.sa_handler = SIG_IGN; |
---|
376 | sigaction(SIGUSR1, &sigact, NULL); |
---|
377 | } |
---|
378 | if (x_running == RUNNING) |
---|
379 | break; |
---|
380 | } |
---|
381 | alarm(0); |
---|
382 | if (x_running != RUNNING) |
---|
383 | { |
---|
384 | syslog(LOG_DEBUG, "Giving up on starting X."); |
---|
385 | console_login(conf, "\nUnable to start X, doing console login " |
---|
386 | "instead.\n"); |
---|
387 | } |
---|
388 | |
---|
389 | /* Tighten up security a little bit. Remove all hosts from X's |
---|
390 | * access control list, assuming /etc/X0.hosts does not exist or |
---|
391 | * has zero length. If it does exist with nonzero length, this |
---|
392 | * behavior is not wanted. The desired effect of removing all hosts |
---|
393 | * is that only connections from the Unix domain socket will be |
---|
394 | * allowed. |
---|
395 | |
---|
396 | * More secure code using Xau also exists, but there wasn't |
---|
397 | * time to completely flesh it out and resolve a couple of |
---|
398 | * issues. This code is probably good enough, but we'll see. |
---|
399 | * Maybe next time. |
---|
400 | |
---|
401 | * This code has the added benefit of leaving an X display |
---|
402 | * connection open, owned by dm. This provides a less-hacky |
---|
403 | * solution to the config_console problem, where if config_console |
---|
404 | * is the first program run on user login, it causes the only |
---|
405 | * X app running at the time, console, to exit, thus resetting |
---|
406 | * the X server. Thus this code also allows the removal of the |
---|
407 | * hack in xlogin that attempts to solve the same problem, but |
---|
408 | * fails on the RS/6000 for reasons unexplored. |
---|
409 | |
---|
410 | * P.S. Don't run this code under Solaris 2.2- (2.3 is safe). |
---|
411 | * Removing all hosts from the acl on that server results in |
---|
412 | * no connections, not even from the Unix domain socket, being |
---|
413 | * allowed. --- cfields |
---|
414 | */ |
---|
415 | |
---|
416 | sprintf(dpyacl, xhosts, dpynum); |
---|
417 | sprintf(dpyname, ":%d", dpynum); |
---|
418 | dpy = XOpenDisplay(dpyname); |
---|
419 | if (dpy != NULL && (stat(dpyacl, &hostsinfo) || hostsinfo.st_size == 0)) |
---|
420 | { |
---|
421 | hosts = XListHosts(dpy, &nhosts, &state); |
---|
422 | if (hosts != NULL) |
---|
423 | { |
---|
424 | XRemoveHosts(dpy, hosts, nhosts); |
---|
425 | XFlush(dpy); |
---|
426 | XFree(hosts); |
---|
427 | } |
---|
428 | } |
---|
429 | /* else if (dpy == NULL) |
---|
430 | * Could've sworn the X server was running now. |
---|
431 | * Follow the original code path. No need introducing new bugs |
---|
432 | * to this hairy code, just preserve the old behavior as though |
---|
433 | * this code had never been added. |
---|
434 | */ |
---|
435 | |
---|
436 | /* set up the console pty */ |
---|
437 | if (openpty(&console_tty, &ttyfd, loginttyname, NULL, NULL)==-1) |
---|
438 | console_login(conf, "Cannot allocate pseudo-terminal\n"); |
---|
439 | |
---|
440 | /* start up console */ |
---|
441 | start_console(console_tty, consoleargv, redir); |
---|
442 | |
---|
443 | /* Fire up the X login */ |
---|
444 | for (tries = 0; tries < 3; tries++) |
---|
445 | { |
---|
446 | syslog(LOG_DEBUG, "Starting xlogin, try #%d", tries + 1); |
---|
447 | login_running = STARTUP; |
---|
448 | sigact.sa_handler = loginready; |
---|
449 | sigaction(SIGUSR1, &sigact, NULL); |
---|
450 | switch (fork_and_store(&loginpid)) |
---|
451 | { |
---|
452 | case 0: |
---|
453 | max_fd = sysconf(_SC_OPEN_MAX); |
---|
454 | for (file = 0; file < max_fd; file++) |
---|
455 | { |
---|
456 | if (file != ttyfd) |
---|
457 | close(file); |
---|
458 | } |
---|
459 | |
---|
460 | login_tty(ttyfd); |
---|
461 | |
---|
462 | file = open("/dev/null", O_RDONLY); |
---|
463 | if (file >= 0) |
---|
464 | { |
---|
465 | dup2(file, 0); |
---|
466 | if (file != 0) |
---|
467 | close(file); |
---|
468 | } |
---|
469 | |
---|
470 | if (redir) |
---|
471 | { |
---|
472 | /* really ought to check the return status of these */ |
---|
473 | #ifdef SRIOCSREDIR |
---|
474 | on = open("/dev/console", O_RDONLY); |
---|
475 | if (on >= 0) |
---|
476 | { |
---|
477 | ioctl(on, SRIOCSREDIR, 1); |
---|
478 | close(on); |
---|
479 | } |
---|
480 | #else |
---|
481 | #ifdef TIOCCONS |
---|
482 | on = 1; |
---|
483 | ioctl(1, TIOCCONS, &on); |
---|
484 | #endif |
---|
485 | #endif |
---|
486 | } |
---|
487 | (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); |
---|
488 | /* ignoring SIGUSR1 will cause xlogin to send us a SIGUSR1 |
---|
489 | * when it is ready |
---|
490 | */ |
---|
491 | sigact.sa_handler = SIG_IGN; |
---|
492 | sigaction(SIGUSR1, &sigact, NULL); |
---|
493 | /* dm ignores sigpipe; because of this, all of the children (ie, */ |
---|
494 | /* the entire session) inherit this unless we fix it now */ |
---|
495 | sigact.sa_handler = SIG_DFL; |
---|
496 | sigaction(SIGPIPE, &sigact, NULL); |
---|
497 | execv(loginargv[0], loginargv); |
---|
498 | fprintf(stderr, "dm: X login failed exec: %s\n", strerror(errno)); |
---|
499 | _exit(1); |
---|
500 | case -1: |
---|
501 | fprintf(stderr, "dm: Unable to fork to start X login: %s\n", |
---|
502 | strerror(errno)); |
---|
503 | break; |
---|
504 | default: |
---|
505 | alarm(LOGIN_START_WAIT); |
---|
506 | alarm_running = RUNNING; |
---|
507 | while (login_running == STARTUP && alarm_running == RUNNING) |
---|
508 | sigsuspend(&sig_zero); |
---|
509 | if (login_running != RUNNING) |
---|
510 | { |
---|
511 | syslog(LOG_DEBUG, "xlogin failed to start; alarm_running=%d", |
---|
512 | alarm_running); |
---|
513 | kill(loginpid, SIGKILL); |
---|
514 | if (alarm_running != NONEXISTENT) |
---|
515 | fprintf(stderr, "dm: Unable to start Xlogin\n"); |
---|
516 | else |
---|
517 | fprintf(stderr, "dm: Xlogin failed to become ready\n"); |
---|
518 | } |
---|
519 | } |
---|
520 | if (login_running == RUNNING) |
---|
521 | break; |
---|
522 | } |
---|
523 | sigact.sa_handler = SIG_IGN; |
---|
524 | sigaction(SIGUSR1, &sigact, NULL); |
---|
525 | alarm(0); |
---|
526 | if (login_running != RUNNING) |
---|
527 | { |
---|
528 | syslog(LOG_DEBUG, "Giving up on starting xlogin."); |
---|
529 | console_login(conf, "\nUnable to start xlogin, doing console login " |
---|
530 | "instead.\n"); |
---|
531 | } |
---|
532 | |
---|
533 | /* main loop. Wait for SIGCHLD, waking up every minute anyway. */ |
---|
534 | (void) sigemptyset(&sig_cur); |
---|
535 | (void) sigaddset(&sig_cur, SIGCHLD); |
---|
536 | (void) sigprocmask(SIG_BLOCK, &sig_cur, NULL); |
---|
537 | while (1) |
---|
538 | { |
---|
539 | /* Wait for something to hapen */ |
---|
540 | if (console_failed) |
---|
541 | { |
---|
542 | /* if no console is running, we must copy bits from the console |
---|
543 | * (master side of pty) to the real console to appear as black |
---|
544 | * bar messages. |
---|
545 | */ |
---|
546 | FD_ZERO(&readfds); |
---|
547 | FD_SET(console_tty, &readfds); |
---|
548 | (void) sigprocmask(SIG_SETMASK, &sig_zero, &mask); |
---|
549 | count = select(console_tty + 1, &readfds, NULL, NULL, NULL); |
---|
550 | (void) sigprocmask(SIG_BLOCK, &mask, NULL); |
---|
551 | if (count > 0 && FD_ISSET(console_tty, &readfds)) |
---|
552 | { |
---|
553 | file = read(console_tty, buf, sizeof(buf)); |
---|
554 | if (file != -1) |
---|
555 | write(1, buf, file); |
---|
556 | } |
---|
557 | } |
---|
558 | else |
---|
559 | { |
---|
560 | alarm(60); |
---|
561 | sigsuspend(&sig_zero); |
---|
562 | } |
---|
563 | |
---|
564 | if (login_running == STARTUP) |
---|
565 | { |
---|
566 | (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); |
---|
567 | console_login(conf, "\nConsole login requested.\n"); |
---|
568 | } |
---|
569 | if (console_running == FAILED) |
---|
570 | { |
---|
571 | console_running = NONEXISTENT; |
---|
572 | time(&now); |
---|
573 | if (now - last_console_failure <= 3) |
---|
574 | { |
---|
575 | /* Give up on console. Set the console characteristics so |
---|
576 | * we don't lose later. */ |
---|
577 | syslog(LOG_ERR, "Giving up on the console"); |
---|
578 | setpgid(0, pgrp = getpid()); /* Reset the tty pgrp */ |
---|
579 | tcsetpgrp(0, pgrp); |
---|
580 | console_failed = TRUE; |
---|
581 | } |
---|
582 | else |
---|
583 | last_console_failure = now; |
---|
584 | } |
---|
585 | if (console_running == NONEXISTENT && !console_failed) |
---|
586 | start_console(console_tty, consoleargv, redir); |
---|
587 | if (login_running == NONEXISTENT || x_running == NONEXISTENT) |
---|
588 | { |
---|
589 | syslog(LOG_DEBUG, "login_running=%d, x_running=%d, quitting", |
---|
590 | login_running, x_running); |
---|
591 | (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); |
---|
592 | cleanup(logintty); |
---|
593 | x_stop_wait(); |
---|
594 | _exit(0); |
---|
595 | } |
---|
596 | } |
---|
597 | } |
---|
598 | |
---|
599 | |
---|
600 | /* Start a login on the raw console */ |
---|
601 | |
---|
602 | static void console_login(char *conf, char *msg) |
---|
603 | { |
---|
604 | int i, cfirst = TRUE, pgrp; |
---|
605 | char *nl = "\r\n"; |
---|
606 | struct termios ttybuf; |
---|
607 | char *p, **cargv; |
---|
608 | int fd; |
---|
609 | |
---|
610 | syslog(LOG_DEBUG, "Performing console login: %s", msg); |
---|
611 | sigemptyset(&sig_zero); |
---|
612 | if (login_running != NONEXISTENT && login_running != STARTUP) |
---|
613 | kill(loginpid, SIGKILL); |
---|
614 | if (console_running != NONEXISTENT) |
---|
615 | { |
---|
616 | if (cfirst) |
---|
617 | kill(consolepid, SIGHUP); |
---|
618 | else |
---|
619 | kill(consolepid, SIGKILL); |
---|
620 | cfirst = FALSE; |
---|
621 | } |
---|
622 | if (x_running != NONEXISTENT) |
---|
623 | kill(xpid, SIGTERM); |
---|
624 | |
---|
625 | x_stop_wait(); |
---|
626 | |
---|
627 | p = getconf(conf, "ttylogin"); |
---|
628 | if (p == NULL) |
---|
629 | { |
---|
630 | fprintf(stderr, "dm: Can't find login command line\n"); |
---|
631 | exit(1); |
---|
632 | } |
---|
633 | cargv = parseargs(p, NULL, NULL, NULL); |
---|
634 | |
---|
635 | setpgid(0, pgrp = 0); /* We have to reset the tty pgrp */ |
---|
636 | tcsetpgrp(0, pgrp); |
---|
637 | tcflush(0, TCIOFLUSH); |
---|
638 | |
---|
639 | (void) tcgetattr(0, &ttybuf); |
---|
640 | ttybuf.c_lflag |= (ICANON | ISIG | ECHO); |
---|
641 | (void) tcsetattr(0, TCSADRAIN, &ttybuf); |
---|
642 | (void) sigprocmask(SIG_SETMASK, &sig_zero, NULL); |
---|
643 | max_fd = sysconf(_SC_OPEN_MAX); |
---|
644 | |
---|
645 | for (i = 3; i < max_fd; i++) |
---|
646 | close(i); |
---|
647 | |
---|
648 | if (msg) |
---|
649 | fprintf(stderr, "%s", msg); |
---|
650 | else |
---|
651 | fprintf(stderr, "%s", nl); |
---|
652 | |
---|
653 | fd = open("/dev/console", O_RDWR); |
---|
654 | if (fd >= 0) |
---|
655 | login_tty(fd); |
---|
656 | |
---|
657 | execv(p, cargv); |
---|
658 | |
---|
659 | fprintf(stderr, "dm: Unable to start console login: %s\n", strerror(errno)); |
---|
660 | _exit(1); |
---|
661 | } |
---|
662 | |
---|
663 | |
---|
664 | /* Start the console program. It will have stdin set to the controling |
---|
665 | * side of the console pty, and stdout set to /dev/console inherited |
---|
666 | * from the display manager. |
---|
667 | */ |
---|
668 | |
---|
669 | static void start_console(int fd, char **argv, int redir) |
---|
670 | { |
---|
671 | int file; |
---|
672 | |
---|
673 | syslog(LOG_DEBUG, "Starting console"); |
---|
674 | |
---|
675 | /* Create console log file owned by daemon */ |
---|
676 | if (access(consolelog, F_OK) != 0) |
---|
677 | { |
---|
678 | file = open(consolelog, O_CREAT, 0644); |
---|
679 | close(file); |
---|
680 | } |
---|
681 | chown(consolelog, DAEMON, 0); |
---|
682 | |
---|
683 | console_running = RUNNING; |
---|
684 | switch (fork_and_store(&consolepid)) |
---|
685 | { |
---|
686 | case 0: |
---|
687 | /* Close all file descriptors except stdout/stderr */ |
---|
688 | close(0); |
---|
689 | max_fd = sysconf(_SC_OPEN_MAX); |
---|
690 | for (file = 3; file < max_fd; file++) |
---|
691 | if (file != fd) |
---|
692 | close(file); |
---|
693 | setsid(); |
---|
694 | dup2(fd, 0); |
---|
695 | close(fd); |
---|
696 | |
---|
697 | setgid(DAEMON); |
---|
698 | setuid(DAEMON); |
---|
699 | (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); |
---|
700 | execv(argv[0], argv); |
---|
701 | fprintf(stderr, "dm: Failed to exec console: %s\n", strerror(errno)); |
---|
702 | _exit(1); |
---|
703 | case -1: |
---|
704 | fprintf(stderr, "dm: Unable to fork to start console: %s\n", |
---|
705 | strerror(errno)); |
---|
706 | _exit(1); |
---|
707 | default: |
---|
708 | writepid(consolepidf, consolepid); |
---|
709 | } |
---|
710 | } |
---|
711 | |
---|
712 | /* Kill children and hang around forever */ |
---|
713 | |
---|
714 | static void shutdown(int signo) |
---|
715 | { |
---|
716 | int pgrp, i; |
---|
717 | struct termios tc; |
---|
718 | char buf[BUFSIZ]; |
---|
719 | int notused; |
---|
720 | |
---|
721 | syslog(LOG_DEBUG, "Received SIGFPE, performing shutdown"); |
---|
722 | if (login_running == RUNNING) |
---|
723 | kill(loginpid, SIGHUP); |
---|
724 | if (console_running == RUNNING) |
---|
725 | kill(consolepid, SIGHUP); |
---|
726 | if (x_running == RUNNING) |
---|
727 | kill(xpid, SIGTERM); |
---|
728 | |
---|
729 | setpgid(0, pgrp = 0); /* We have to reset the tty pgrp */ |
---|
730 | tcsetpgrp(0, pgrp); |
---|
731 | tcflush(0, TCIOFLUSH); |
---|
732 | |
---|
733 | (void) tcgetattr(0, &tc); |
---|
734 | tc.c_lflag |= (ICANON | ISIG | ECHO); |
---|
735 | (void) tcsetattr(0, TCSADRAIN, &tc); |
---|
736 | |
---|
737 | (void) sigprocmask(SIG_SETMASK, &sig_zero, (sigset_t *) 0); |
---|
738 | |
---|
739 | /* Make sure the slave side is open before we read from the master; |
---|
740 | * some systems will return EIO if there are no current writers. |
---|
741 | */ |
---|
742 | sprintf(buf, "/dev/%s", logintty); |
---|
743 | notused = open(buf, O_RDWR, 0); |
---|
744 | while (1) |
---|
745 | { |
---|
746 | i = read(console_tty, buf, sizeof(buf)); |
---|
747 | if (i == -1 && errno != EINTR) |
---|
748 | perror("console pty"); |
---|
749 | else if (i > 0) |
---|
750 | write(1, buf, i); |
---|
751 | } |
---|
752 | } |
---|
753 | |
---|
754 | |
---|
755 | /* Kill children, remove password entry */ |
---|
756 | |
---|
757 | static void cleanup(char *tty) |
---|
758 | { |
---|
759 | int file, ret; |
---|
760 | struct utmp utmp; |
---|
761 | char login[sizeof(utmp.ut_name) + 1]; |
---|
762 | char *errr; |
---|
763 | |
---|
764 | if (login_running == RUNNING) |
---|
765 | kill(loginpid, SIGHUP); |
---|
766 | if (console_running == RUNNING) |
---|
767 | kill(consolepid, SIGHUP); |
---|
768 | if (x_running == RUNNING) |
---|
769 | kill(xpid, SIGTERM); |
---|
770 | |
---|
771 | /* Find out what the login name was, so we can feed it to libal. */ |
---|
772 | login[0] = '\0'; |
---|
773 | if ((file = open(utmpf, O_RDWR, 0)) >= 0) |
---|
774 | { |
---|
775 | while (read(file, (char *) &utmp, sizeof(utmp)) > 0) |
---|
776 | { |
---|
777 | if (!strncmp(utmp.ut_line, tty, sizeof(utmp.ut_line))) |
---|
778 | { |
---|
779 | strncpy(login, utmp.ut_name, sizeof(utmp.ut_name)); |
---|
780 | login[sizeof(utmp.ut_name)] = '\0'; |
---|
781 | } |
---|
782 | } |
---|
783 | close(file); |
---|
784 | } |
---|
785 | |
---|
786 | /* Update the utmp & wtmp. */ |
---|
787 | |
---|
788 | logout(tty); |
---|
789 | |
---|
790 | if (login[0]) |
---|
791 | { |
---|
792 | ret = al_acct_revert(login, loginpid); |
---|
793 | if (ret != AL_SUCCESS) |
---|
794 | { |
---|
795 | syslog(LOG_ERR, "al_acct_revert(%s, %d): %s", login, loginpid, |
---|
796 | al_strerror(ret, &errr)); |
---|
797 | al_free_errmem(errr); |
---|
798 | } |
---|
799 | } |
---|
800 | |
---|
801 | tcflush(0, TCIOFLUSH); |
---|
802 | } |
---|
803 | |
---|
804 | |
---|
805 | /* When we get sigchild, figure out which child it was and set |
---|
806 | * appropriate flags |
---|
807 | */ |
---|
808 | |
---|
809 | static void child(int signo) |
---|
810 | { |
---|
811 | int pid; |
---|
812 | int status; |
---|
813 | struct sigaction sigact; |
---|
814 | |
---|
815 | sigemptyset(&sigact.sa_mask); |
---|
816 | sigact.sa_flags = 0; |
---|
817 | sigact.sa_handler = child; |
---|
818 | sigaction(SIGCHLD, &sigact, NULL); |
---|
819 | |
---|
820 | while (1) |
---|
821 | { |
---|
822 | pid = waitpid(-1, &status, WNOHANG); |
---|
823 | if (pid == 0 || pid == -1) |
---|
824 | return; |
---|
825 | |
---|
826 | if (pid == xpid) |
---|
827 | { |
---|
828 | syslog(LOG_DEBUG, "Received SIGCHLD for xpid (%d), status %d", pid, |
---|
829 | status); |
---|
830 | x_running = NONEXISTENT; |
---|
831 | } |
---|
832 | else if (pid == consolepid) |
---|
833 | { |
---|
834 | syslog(LOG_DEBUG, |
---|
835 | "Received SIGCHLD for consolepid (%d), status %d", pid, |
---|
836 | status); |
---|
837 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) |
---|
838 | console_running = FAILED; |
---|
839 | else |
---|
840 | console_running = NONEXISTENT; |
---|
841 | } |
---|
842 | else if (pid == loginpid) |
---|
843 | { |
---|
844 | syslog(LOG_DEBUG, "Received SIGCHLD for loginpid (%d), status %d", |
---|
845 | pid, status); |
---|
846 | if (WEXITSTATUS(status) == CONSOLELOGIN) |
---|
847 | login_running = STARTUP; |
---|
848 | else |
---|
849 | login_running = NONEXISTENT; |
---|
850 | } |
---|
851 | else |
---|
852 | { |
---|
853 | syslog(LOG_DEBUG, "Received SIGCHLD for unknown pid %d", pid); |
---|
854 | fprintf(stderr, "dm: Unexpected SIGCHLD from pid %d\n", pid); |
---|
855 | } |
---|
856 | } |
---|
857 | } |
---|
858 | |
---|
859 | |
---|
860 | static void xready(int signo) |
---|
861 | { |
---|
862 | syslog(LOG_DEBUG, "Received SIGUSR1; setting x_running."); |
---|
863 | x_running = RUNNING; |
---|
864 | } |
---|
865 | |
---|
866 | static void loginready(int signo) |
---|
867 | { |
---|
868 | syslog(LOG_DEBUG, "Received SIGUSR1; setting login_running."); |
---|
869 | login_running = RUNNING; |
---|
870 | } |
---|
871 | |
---|
872 | /* When an alarm happens, just note it and return */ |
---|
873 | |
---|
874 | static void catchalarm(int signo) |
---|
875 | { |
---|
876 | syslog(LOG_DEBUG, "Received SIGALRM."); |
---|
877 | alarm_running = NONEXISTENT; |
---|
878 | } |
---|
879 | |
---|
880 | |
---|
881 | /* kill children and go away */ |
---|
882 | |
---|
883 | static void die(int signo) |
---|
884 | { |
---|
885 | syslog(LOG_DEBUG, "Dying on signal %d", signo); |
---|
886 | cleanup(logintty); |
---|
887 | _exit(0); |
---|
888 | } |
---|
889 | |
---|
890 | /* Takes a command line, returns an argv-style NULL terminated array |
---|
891 | * of strings. The array is in malloc'ed memory. |
---|
892 | */ |
---|
893 | |
---|
894 | static char **parseargs(char *line, char *extra, char *extra1, char *extra2) |
---|
895 | { |
---|
896 | int i = 0; |
---|
897 | char *p = line; |
---|
898 | char **ret; |
---|
899 | |
---|
900 | while (*p) |
---|
901 | { |
---|
902 | while (*p && isspace((unsigned char)*p)) |
---|
903 | p++; |
---|
904 | while (*p && !isspace((unsigned char)*p)) |
---|
905 | p++; |
---|
906 | i++; |
---|
907 | } |
---|
908 | ret = (char **) malloc(sizeof(char *) * (i + 4)); |
---|
909 | |
---|
910 | p = line; |
---|
911 | i = 0; |
---|
912 | while (*p) |
---|
913 | { |
---|
914 | while (*p && isspace((unsigned char)*p)) |
---|
915 | p++; |
---|
916 | if (*p == 0) |
---|
917 | break; |
---|
918 | ret[i++] = p; |
---|
919 | while (*p && !isspace((unsigned char)*p)) |
---|
920 | p++; |
---|
921 | if (*p == 0) |
---|
922 | break; |
---|
923 | *p++ = 0; |
---|
924 | } |
---|
925 | if (extra) |
---|
926 | ret[i++] = extra; |
---|
927 | if (extra1) |
---|
928 | ret[i++] = extra1; |
---|
929 | if (extra2) |
---|
930 | ret[i++] = extra2; |
---|
931 | |
---|
932 | ret[i] = NULL; |
---|
933 | return (ret); |
---|
934 | } |
---|
935 | |
---|
936 | /* Find a named field in the config file. Config file contains |
---|
937 | * comment lines starting with #, and lines with a field name, |
---|
938 | * whitespace, then field value. This routine returns the field |
---|
939 | * value, or NULL on error. |
---|
940 | */ |
---|
941 | |
---|
942 | static char *getconf(char *file, char *name) |
---|
943 | { |
---|
944 | static char buf[8192]; |
---|
945 | static int inited = 0; |
---|
946 | char *p, *ret; |
---|
947 | int i; |
---|
948 | |
---|
949 | if (!inited) |
---|
950 | { |
---|
951 | int fd; |
---|
952 | |
---|
953 | fd = open(file, O_RDONLY, 0644); |
---|
954 | if (fd < 0) |
---|
955 | return (NULL); |
---|
956 | i = read(fd, buf, sizeof(buf)); |
---|
957 | if (i >= sizeof(buf) - 1) |
---|
958 | fprintf(stderr, "dm: warning - config file is to long to parse\n"); |
---|
959 | buf[i] = 0; |
---|
960 | close(fd); |
---|
961 | inited = 1; |
---|
962 | } |
---|
963 | |
---|
964 | for (p = &buf[0]; p && *p; p = strchr(p, '\n')) |
---|
965 | { |
---|
966 | if (*p == '\n') |
---|
967 | p++; |
---|
968 | if (p == NULL || *p == 0) |
---|
969 | return (NULL); |
---|
970 | if (*p == '#') |
---|
971 | continue; |
---|
972 | if (strncmp(p, name, strlen(name))) |
---|
973 | continue; |
---|
974 | p += strlen(name); |
---|
975 | if (*p && !isspace((unsigned char)*p)) |
---|
976 | continue; |
---|
977 | while (*p && isspace((unsigned char)*p)) |
---|
978 | p++; |
---|
979 | if (*p == 0) |
---|
980 | return (NULL); |
---|
981 | ret = strchr(p, '\n'); |
---|
982 | if (ret) |
---|
983 | i = ret - p; |
---|
984 | else |
---|
985 | i = strlen(p); |
---|
986 | ret = malloc(i + 1); |
---|
987 | memcpy(ret, p, i + 1); |
---|
988 | ret[i] = 0; |
---|
989 | return (ret); |
---|
990 | } |
---|
991 | return (NULL); |
---|
992 | } |
---|
993 | |
---|
994 | /* Fork, storing the pid in a variable var and returning the pid. |
---|
995 | * Make sure that the pid is stored before any SIGCHLD can be |
---|
996 | * delivered. |
---|
997 | */ |
---|
998 | static pid_t fork_and_store(pid_t * var) |
---|
999 | { |
---|
1000 | sigset_t mask, omask; |
---|
1001 | pid_t pid; |
---|
1002 | |
---|
1003 | sigemptyset(&mask); |
---|
1004 | sigaddset(&mask, SIGCHLD); |
---|
1005 | sigprocmask(SIG_BLOCK, &mask, &omask); |
---|
1006 | pid = fork(); |
---|
1007 | *var = pid; |
---|
1008 | sigprocmask(SIG_SETMASK, &omask, NULL); |
---|
1009 | return pid; |
---|
1010 | } |
---|
1011 | |
---|
1012 | static void x_stop_wait(void) |
---|
1013 | { |
---|
1014 | sigset_t mask, omask; |
---|
1015 | |
---|
1016 | sigfillset(&mask); |
---|
1017 | sigdelset(&mask, SIGALRM); |
---|
1018 | sigprocmask(SIG_BLOCK, &mask, &omask); |
---|
1019 | alarm(0); |
---|
1020 | while (waitpid(xpid, NULL, 0) == -1 && errno == EINTR) |
---|
1021 | ; |
---|
1022 | x_running = NONEXISTENT; |
---|
1023 | sigprocmask(SIG_SETMASK, &omask, NULL); |
---|
1024 | } |
---|
1025 | |
---|
1026 | /* Write a pid to a file */ |
---|
1027 | static void writepid(char *file, pid_t pid) |
---|
1028 | { |
---|
1029 | FILE *fp; |
---|
1030 | |
---|
1031 | fp = fopen(file, "w"); |
---|
1032 | |
---|
1033 | if (fp == NULL) |
---|
1034 | return; |
---|
1035 | |
---|
1036 | fprintf(fp, "%lu\n", (unsigned long)pid); |
---|
1037 | fclose(fp); |
---|
1038 | } |
---|
1039 | |
---|
1040 | #ifndef HAVE_LOGOUT |
---|
1041 | #ifdef HAVE_PUTUTLINE |
---|
1042 | static void logout(const char *line) |
---|
1043 | { |
---|
1044 | struct utmp utmp; |
---|
1045 | struct utmp *putmp; |
---|
1046 | pid_t pid; |
---|
1047 | #ifdef HAVE_PUTUTXLINE |
---|
1048 | struct utmpx utmpx; |
---|
1049 | #endif |
---|
1050 | #ifndef HAVE_UPDWTMP |
---|
1051 | int file; |
---|
1052 | #endif |
---|
1053 | |
---|
1054 | strcpy(utmp.ut_line, line); |
---|
1055 | setutent(); |
---|
1056 | putmp = getutline(&utmp); |
---|
1057 | if (putmp != NULL) |
---|
1058 | { |
---|
1059 | time(&utmp.ut_time); |
---|
1060 | strncpy(utmp.ut_line, putmp->ut_line, sizeof(utmp.ut_line)); |
---|
1061 | strncpy(utmp.ut_user, putmp->ut_name, sizeof(utmp.ut_name)); |
---|
1062 | pid = getpid(); |
---|
1063 | utmp.ut_pid = pid; |
---|
1064 | strncpy(utmp.ut_id, putmp->ut_id, sizeof(utmp.ut_id)); |
---|
1065 | utmp.ut_type = DEAD_PROCESS; |
---|
1066 | pututline(&utmp); |
---|
1067 | #ifdef HAVE_PUTUTXLINE |
---|
1068 | getutmpx(&utmp, &utmpx); |
---|
1069 | utmpx.ut_pid = pid; |
---|
1070 | setutxent(); |
---|
1071 | pututxline(&utmpx); |
---|
1072 | endutxent(); |
---|
1073 | #endif /* HAVE_PUTUTXLINE */ |
---|
1074 | |
---|
1075 | updwtmp(wtmpf, &utmp); |
---|
1076 | } |
---|
1077 | endutent(); |
---|
1078 | } |
---|
1079 | #endif |
---|
1080 | #endif |
---|
1081 | |
---|
1082 | #ifndef HAVE_LOGIN_TTY |
---|
1083 | static int login_tty(int fd) |
---|
1084 | { |
---|
1085 | #ifndef TIOCSCTTY |
---|
1086 | char *name; |
---|
1087 | #endif |
---|
1088 | int ttyfd; |
---|
1089 | |
---|
1090 | setsid(); |
---|
1091 | |
---|
1092 | #ifdef TIOCSCTTY |
---|
1093 | if (ioctl(fd, TIOCSCTTY, NULL) == -1) |
---|
1094 | return(-1); |
---|
1095 | ttyfd = fd; |
---|
1096 | #else |
---|
1097 | name = ttyname(fd); |
---|
1098 | if (ttyname == NULL) |
---|
1099 | return(-1); |
---|
1100 | close(fd); |
---|
1101 | ttyfd = open(name, O_RDWR); |
---|
1102 | #endif |
---|
1103 | |
---|
1104 | if (ttyfd == -1) |
---|
1105 | return(-1); |
---|
1106 | |
---|
1107 | dup2(ttyfd, STDIN_FILENO); |
---|
1108 | dup2(ttyfd, STDOUT_FILENO); |
---|
1109 | dup2(ttyfd, STDERR_FILENO); |
---|
1110 | |
---|
1111 | if (ttyfd > STDERR_FILENO) |
---|
1112 | close(ttyfd); |
---|
1113 | |
---|
1114 | return(0); |
---|
1115 | } |
---|
1116 | #endif |
---|
1117 | |
---|
1118 | #ifndef HAVE_OPENPTY |
---|
1119 | #if HAVE__GETPTY |
---|
1120 | /* Oooh, we're on an sgi. */ |
---|
1121 | static int openpty(int *amaster, int *aslave, char *name, |
---|
1122 | struct termios *termp, struct winsize *winp) |
---|
1123 | { |
---|
1124 | char *p; |
---|
1125 | |
---|
1126 | p = _getpty(amaster, O_RDWR, 0600, 0); |
---|
1127 | |
---|
1128 | if (p == NULL) |
---|
1129 | return(-1); |
---|
1130 | |
---|
1131 | if (name != NULL) |
---|
1132 | strcpy(name, p); |
---|
1133 | |
---|
1134 | *aslave = open(p, O_RDWR); |
---|
1135 | if (*aslave < 0) |
---|
1136 | { |
---|
1137 | close(*amaster); |
---|
1138 | return(-1); |
---|
1139 | } |
---|
1140 | |
---|
1141 | if (termsetup(*aslave, termp, winp) < 0) |
---|
1142 | { |
---|
1143 | close(*amaster); |
---|
1144 | close(*aslave); |
---|
1145 | return(-1); |
---|
1146 | } |
---|
1147 | |
---|
1148 | return(0); |
---|
1149 | } |
---|
1150 | #elif HAVE_GRANTPT |
---|
1151 | static int openpty(int *amaster, int *aslave, char *name, |
---|
1152 | struct termios *termp, struct winsize *winp) |
---|
1153 | { |
---|
1154 | char *p; |
---|
1155 | |
---|
1156 | *amaster = open("/dev/ptmx", O_RDWR); |
---|
1157 | if (*amaster < 0) |
---|
1158 | return(-1); |
---|
1159 | |
---|
1160 | if (grantpt(*amaster) < 0) |
---|
1161 | { |
---|
1162 | close(*amaster); |
---|
1163 | return(-1); |
---|
1164 | } |
---|
1165 | |
---|
1166 | if (unlockpt(*amaster) < 0) |
---|
1167 | { |
---|
1168 | close(*amaster); |
---|
1169 | return(-1); |
---|
1170 | } |
---|
1171 | |
---|
1172 | p = ptsname(*amaster); |
---|
1173 | |
---|
1174 | if (name != NULL) |
---|
1175 | strcpy(name, p); |
---|
1176 | |
---|
1177 | *aslave = open(p, O_RDWR); |
---|
1178 | if(*aslave < 0) |
---|
1179 | { |
---|
1180 | close(*amaster); |
---|
1181 | return(-1); |
---|
1182 | } |
---|
1183 | |
---|
1184 | if (ioctl(*aslave, I_PUSH, "ptem") < 0) |
---|
1185 | |
---|
1186 | { |
---|
1187 | close(*amaster); |
---|
1188 | close(*aslave); |
---|
1189 | return(-1); |
---|
1190 | } |
---|
1191 | |
---|
1192 | if (ioctl(*aslave, I_PUSH, "ldterm") < 0) |
---|
1193 | { |
---|
1194 | close(*amaster); |
---|
1195 | close(*aslave); |
---|
1196 | return(-1); |
---|
1197 | } |
---|
1198 | |
---|
1199 | if (ioctl(*aslave, I_PUSH, "ttcompat") < 0) |
---|
1200 | { |
---|
1201 | close(*amaster); |
---|
1202 | close(*aslave); |
---|
1203 | return(-1); |
---|
1204 | } |
---|
1205 | |
---|
1206 | if (termsetup(*aslave, termp, winp) < 0) |
---|
1207 | { |
---|
1208 | close(*amaster); |
---|
1209 | close(*aslave); |
---|
1210 | return(-1); |
---|
1211 | } |
---|
1212 | |
---|
1213 | return(0); |
---|
1214 | } |
---|
1215 | #else |
---|
1216 | /* traditional bsd way */ |
---|
1217 | static int openpty(int *amaster, int *aslave, char *name, |
---|
1218 | struct termios *termp, struct winsize *winp); |
---|
1219 | { |
---|
1220 | char s[20]; |
---|
1221 | char *p, *q; |
---|
1222 | |
---|
1223 | *amaster == -1; |
---|
1224 | |
---|
1225 | strcpy(s, "/dev/ptyxx"); |
---|
1226 | for (p = "pqrstuvwxyzPQRST"; *p; p++) |
---|
1227 | { |
---|
1228 | s[8] = *p; |
---|
1229 | for(q = "0123456789abcdef"; *q; q++) |
---|
1230 | { |
---|
1231 | s[9] = *q; |
---|
1232 | |
---|
1233 | *amaster = open(s, O_RDWR); |
---|
1234 | if (*amaster == ENOENT) |
---|
1235 | return(-1); |
---|
1236 | else |
---|
1237 | continue; |
---|
1238 | |
---|
1239 | s[5]='t'; |
---|
1240 | |
---|
1241 | break; |
---|
1242 | } |
---|
1243 | |
---|
1244 | if (*amaster != -1) |
---|
1245 | break; |
---|
1246 | } |
---|
1247 | |
---|
1248 | if (name != NULL) |
---|
1249 | strcpy(s, name); |
---|
1250 | |
---|
1251 | *aslave = open(s, O_RDWR); |
---|
1252 | |
---|
1253 | if (*aslave < 0) |
---|
1254 | { |
---|
1255 | close(*amaster); |
---|
1256 | return(-1); |
---|
1257 | } |
---|
1258 | |
---|
1259 | |
---|
1260 | if (termsetup(*aslave, termp, winp) < 0) |
---|
1261 | { |
---|
1262 | close(*amaster); |
---|
1263 | close(*aslave); |
---|
1264 | return(-1); |
---|
1265 | } |
---|
1266 | |
---|
1267 | return(0); |
---|
1268 | } |
---|
1269 | #endif |
---|
1270 | |
---|
1271 | static int termsetup(int fd, struct termios *termp, struct winsize *winp) |
---|
1272 | { |
---|
1273 | if (termp != NULL) |
---|
1274 | if (tcsetattr(fd, TCSANOW, termp) < 0) |
---|
1275 | return(-1); |
---|
1276 | |
---|
1277 | if (winp != NULL) |
---|
1278 | if (ioctl(fd, TIOCSWINSZ, winp) < 0) |
---|
1279 | return(-1); |
---|
1280 | |
---|
1281 | return(0); |
---|
1282 | } |
---|
1283 | #endif |
---|