1 
2 /*
3  * acclogin is an attempt at an accessible login prompt for Solaris.
4  * It should be used in conjunction with yasr (Yet Another Screen Reader)
5  * and a text-to-speech synthesizer.
6  *
7  * This program does the following:
8  * - opens "/dev/console" on file descriptors 0, 1 and 2,
9  * - adjusts/creates a utmp entry for "console" as a LOGIN_PROCESS.
10  * - fork's and exec's yasr with possible command line options.
11  * - waits for the child (yasr) propcess to terminate.
12  * - adjusts the utmp entry for "console" to be a DEAD_PROCESS.
13  *
14  * Copyright (C) 2002 by Rich Burridge, Sun Microsystems Inc.
15  *
16  * acclogin comes with ABSOLUTELY NO WARRANTY.
17  *
18  * This is free software, placed under the terms of the
19  * GNU Lesser General Public License, as published by the Free Software
20  * Foundation.  Please see the file COPYING for details.
21  */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <syslog.h>
30 #include <utmpx.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 #include <fcntl.h>
35 #include <time.h>
36 
37 #define FAILED      2
38 #define MAXARGS     20
39 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
40 
41 #define CLOSE   (void) close
42 #define DUP     (void) dup
43 #define EXECVE  (void) execve
44 
45 static int yasr_pid;
46 
47 static char *yasr_argv[MAXARGS];
48 
49 /* Command line options. */
50 static char *config_file = NULL;
51 static char *login_prog = NULL;
52 static char *port = NULL;
53 static char *synthesizer = NULL;
54 static char *yasr_prog = NULL;
55 
56 extern char **environ;
57 
58 /* debug.c prototypes */
59 extern void open_debug(char *);
60 extern void debug(char *format, ...);
61 extern void close_debug();
62 
63 
64 static void
set_utmp_entry(char * user,char * ttyn,int type)65 set_utmp_entry(char *user, char *ttyn, int type)
66 {
67     struct utmpx *u = (struct utmpx *) 0;
68     struct utmpx utmp;
69     char *ttyntail = NULL;
70 
71     setutxent();
72     (void) memset((void *)&utmp, 0, sizeof(utmp));
73 
74     (void) time(&utmp.ut_tv.tv_sec);
75     utmp.ut_pid = getpid();
76 
77     (void) memset(utmp.ut_host, 0, sizeof(utmp.ut_host));
78 
79     (void) strcpy(utmp.ut_user, "root");
80 
81     if (ttyn) {
82         if (strstr(ttyn, "/dev/") != NULL) {
83             ttyntail = strdup(ttyn + sizeof ("/dev/") - 1);
84         } else {
85             ttyntail = strdup(ttyn);
86         }
87     }
88 
89     SCPYN(utmp.ut_line, ttyntail);
90     utmp.ut_type = type;
91 
92     while ((u = getutxent()) != NULL) {
93         if ((u->ut_type == INIT_PROCESS ||
94              u->ut_type == LOGIN_PROCESS ||
95              u->ut_type == USER_PROCESS) &&
96              ((ttyntail != NULL &&
97              strncmp(u->ut_line, ttyntail, sizeof(u->ut_line)) == 0) ||
98              u->ut_pid == utmp.ut_pid)) {
99             if (ttyntail) {
100                 SCPYN(utmp.ut_line, ttyntail);
101             }
102 
103             (void) pututxline(&utmp);
104             break;
105         }
106     }
107     endutxent();        /* Close utmp file */
108 
109     if (u == (struct utmpx *) NULL) {
110         (void) makeutx(&utmp);
111     } else {
112         updwtmpx(WTMPX_FILE, &utmp);
113     }
114 
115     if (ttyntail) {
116         free(ttyntail);
117     }
118 }
119 
120 
121 static void
reset_utmp_entry(int pid,int exitcode)122 reset_utmp_entry(int pid, int exitcode)
123 {
124     struct utmpx *up;
125     struct utmpx ut;
126 
127     setutxent();
128     ut.ut_type = DEAD_PROCESS;
129 
130     while ((up = getutxent()) != NULL && (up = getutxid(&ut)) != NULL) {
131         if (up->ut_pid == pid) {
132             if (up->ut_type == DEAD_PROCESS) {
133                 /* Cleaned up elsewhere. */
134                 endutxent();
135                 return;
136             }
137 
138             up->ut_type = DEAD_PROCESS;
139             up->ut_exit.e_termination = exitcode & 0xff;
140             up->ut_exit.e_exit = (exitcode >> 8) & 0xff;
141 
142             (void) time(&up->ut_tv.tv_sec);
143 
144             if (modutx(up) == NULL) {
145                 /* modutx failed so we write out the new entry ourselves. */
146                 (void) pututxline(up);
147                 updwtmpx("wtmpx", up);
148             }
149 
150             endutxent();
151             return;
152         }
153     }
154 
155     endutxent();
156 }
157 
158 
159 static void
set_exec_args()160 set_exec_args()
161 {
162     int i = 0;
163 
164 /* XXX: should really derive the location of the yasr executable from what
165  *      the userr gave as --prefix at configuration time.
166  */
167 
168     yasr_argv[i++] = (yasr_prog != NULL) ? yasr_prog : "/usr/local/bin/yasr";
169 
170     if (config_file != NULL) {
171         yasr_argv[i++] = "-C";
172         yasr_argv[i++] = config_file;
173     }
174 
175     if (synthesizer != NULL) {
176         yasr_argv[i++] = "-s";
177         yasr_argv[i++] = synthesizer;
178     }
179 
180     if (port != NULL) {
181         yasr_argv[i++] = "-p";
182         yasr_argv[i++] = port;
183     }
184 
185     yasr_argv[i++] = (login_prog != NULL) ? login_prog : "/bin/login";
186     yasr_argv[i] = NULL;
187 }
188 
189 
190 /*ARGSUSED*/
191 int
main(int argc,char * argv[])192 main(int argc, char *argv[])
193 {
194     int flag = 1;
195     int exit_code = FAILED;
196     int status, val;
197 
198     while (flag) {
199         switch (getopt(argc, argv, "C:l:s:p:y:")) {
200             case 'C':                          /* Yasr configuration file. */
201                 config_file = strdup(optarg);
202                 break;
203             case 'l':                          /* Alternative login program. */
204                 login_prog = strdup(optarg);
205                 break;
206             case 's':                          /* Yasr synthesizer. */
207                 synthesizer = strdup(optarg);
208                 break;
209             case 'p':                          /* Synthesizer port number. */
210                 port = strdup(optarg);
211                 break;
212             case 'y':                          /* Alternative yasr program. */
213                 yasr_prog = strdup(optarg);
214                 break;
215 
216             default:
217                 flag = 0;
218         }
219     }
220 
221     CLOSE(0);
222     if (open("/dev/console", O_RDWR) != 0) {
223         exit(2);
224     }
225     CLOSE(1);
226     CLOSE(2);
227     DUP(0);
228     DUP(0);
229 
230     set_utmp_entry("", ttyname(0), LOGIN_PROCESS);
231 
232     set_exec_args();
233     if ((yasr_pid = fork()) == 0) {                      /* Child */
234         set_utmp_entry("", ttyname(0), LOGIN_PROCESS);
235         EXECVE(yasr_argv[0], yasr_argv, environ);
236         perror("execve");
237     } else if (yasr_pid != -1) {                               /* Parent. */
238         do {
239             val = wait(&status);
240             if (val == -1) {
241                 if (errno == EINTR) {
242                     continue;
243                 } else {
244                     syslog(LOG_ERR, "wait: %m");   /* Shouldn't happen. */
245                     break;
246                 }
247             }
248         } while (val != yasr_pid);
249 
250         if ((status & 0377) > 0) {
251             exit_code = FAILED;                /* Killed by some signal. */
252         }
253 
254 /* Get the second byte, this is the exit/return code. */
255 
256         exit_code = ((status >> 8) & 0377);
257 
258     } else {
259         perror("fork");
260         exit_code = FAILED;
261     }
262 
263     reset_utmp_entry(getpid(), exit_code);
264     exit(exit_code);
265 }
266