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