1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <signal.h>
37 #include <utmpx.h>
38 #include <pwd.h>
39 #include <dirent.h>
40 #include <sys/param.h>
41 #include <sys/acl.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <sys/mkdev.h>
45 #include <sys/console.h>
46 #include <libdevinfo.h>
47 #include "ttymon.h"
48 #include "tmextern.h"
49 #include "tmstruct.h"
50
51 static char devbuf[BUFSIZ];
52 static char *devname;
53
54 static int parse_args(int, char **, struct pmtab *);
55 static void ttymon_options(int, char **, struct pmtab *);
56 static void getty_options(int, char **, struct pmtab *);
57 static void usage(void);
58 static char *find_ttyname(int);
59
60 /*
61 * ttymon_express - This is call when ttymon is invoked with args
62 * or invoked as getty
63 * - This special version of ttymon will monitor
64 * one port only
65 * - It is intended to be used when some process
66 * wants to have a login session on the fly
67 */
68 void
ttymon_express(int argc,char ** argv)69 ttymon_express(int argc, char **argv)
70 {
71 struct pmtab *pmtab;
72 struct sigaction sigact;
73 #ifdef DEBUG
74 #endif
75
76 #ifdef DEBUG
77 opendebug(TRUE);
78 #endif
79
80 sigact.sa_flags = 0;
81 sigact.sa_handler = SIG_IGN;
82 (void) sigemptyset(&sigact.sa_mask);
83 (void) sigaction(SIGINT, &sigact, NULL);
84
85 if ((pmtab = ALLOC_PMTAB) == NULL) {
86 log("ttymon_express: ALLOC_PMTAB failed");
87 exit(1);
88 }
89
90 if (parse_args(argc, argv, pmtab) != 0) {
91 log("ttymon_express: parse_args failed");
92 exit(1);
93 }
94
95 read_ttydefs(NULL, FALSE);
96
97 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
98 while (checkut_line(pmtab->p_device))
99 (void) sleep(15);
100
101 if ((pmtab->p_device == NULL) || (*(pmtab->p_device) == '\0')) {
102 devname = find_ttyname(0);
103 if ((devname == NULL) || (*devname == '\0')) {
104 log("ttyname cannot find the device on fd 0");
105 exit(1);
106 }
107 pmtab->p_device = devname;
108 #ifdef DEBUG
109 debug("ttymon_express: devname = %s", devname);
110 #endif
111 /*
112 * become session leader
113 * fd 0 is closed and reopened just to make sure
114 * controlling tty is set up right
115 */
116 (void) setsid();
117 (void) close(0);
118 revokedevaccess(pmtab->p_device, 0, 0, 0);
119 if (open(pmtab->p_device, O_RDWR) < 0) {
120 log("open %s failed: %s", pmtab->p_device,
121 strerror(errno));
122 exit(1);
123 }
124 if ((pmtab->p_modules != NULL) &&
125 (*(pmtab->p_modules) != '\0')) {
126 if (push_linedisc(0, pmtab->p_modules,
127 pmtab->p_device) == -1)
128 exit(1);
129 }
130 if (initial_termio(0, pmtab) == -1)
131 exit(1);
132 (void) di_devperm_logout((const char *)pmtab->p_device);
133 } else {
134 (void) setsid();
135 (void) close(0);
136 Retry = FALSE;
137 open_device(pmtab);
138 if (Retry) /* open failed */
139 exit(1);
140 }
141 tmchild(pmtab);
142 exit(1); /*NOTREACHED*/
143 }
144
145 /*
146 * For serial device, return ttyX-mode property value.
147 */
148 static char *
get_ttymode_prop(dev_t rconsdev)149 get_ttymode_prop(dev_t rconsdev)
150 {
151 char *rootpath = "/";
152 char path[MAXPATHLEN];
153 di_node_t root;
154 char *propname, *v;
155 struct stat st;
156
157 (void) snprintf(path, sizeof (path), "/dev/tty%c",
158 'a' + minor(rconsdev));
159 if (stat(path, &st) < 0)
160 return (NULL);
161
162 if (st.st_rdev != rconsdev)
163 return (NULL);
164
165 if (asprintf(&propname, "%s-mode", path + 5) <= 0)
166 return (NULL);
167
168 root = di_init(rootpath, DINFOPROP);
169 if (root == DI_NODE_NIL) {
170 free(propname);
171 return (NULL);
172 }
173
174 v = NULL;
175 if (di_prop_lookup_strings(DDI_DEV_T_ANY, root, propname, &v) > 0)
176 v = strdup(v);
177
178 di_fini(root);
179 free(propname);
180 return (v);
181 }
182
183 /*
184 * parse_arg - parse cmd line arguments
185 */
186 static int
parse_args(int argc,char ** argv,struct pmtab * pmtab)187 parse_args(int argc, char **argv, struct pmtab *pmtab)
188 {
189 static char p_server[] = "/usr/bin/login";
190 static char termbuf[MAX_TERM_TYPE_LEN];
191 static struct cons_getterm cnterm = {sizeof (termbuf), termbuf};
192
193 /* initialize fields to some default first */
194 pmtab->p_tag = "";
195 pmtab->p_flags = 0;
196 pmtab->p_identity = "root";
197 pmtab->p_res1 = "reserved";
198 pmtab->p_res2 = "reserved";
199 pmtab->p_res3 = "reserved";
200 pmtab->p_uid = 0;
201 pmtab->p_gid = 0;
202 pmtab->p_dir = "/";
203 pmtab->p_ttyflags = 0;
204 pmtab->p_count = 0;
205 pmtab->p_server = p_server;
206 pmtab->p_timeout = 0;
207 pmtab->p_modules = "";
208 pmtab->p_prompt = "login: ";
209 pmtab->p_dmsg = "";
210 pmtab->p_termtype = "";
211 pmtab->p_device = "";
212 pmtab->p_status = GETTY;
213 pmtab->p_ttymode = NULL;
214 if (strcmp(lastname(argv[0]), "getty") == 0) {
215 pmtab->p_ttylabel = "300";
216 getty_options(argc, argv, pmtab);
217 } else {
218 int cn_fd;
219 struct cons_getdev cnd;
220
221 pmtab->p_ttylabel = "9600";
222 ttymon_options(argc, argv, pmtab);
223
224 /*
225 * The following code is only reached if -g was specified.
226 * It attempts to determine a suitable terminal type for
227 * the console login process, and in case we are using
228 * serial console, tty mode line.
229 *
230 * If -d /dev/console also specified, we send an ioctl
231 * to the console device to query the TERM type.
232 *
233 * If any of the tests, system calls, or ioctls fail
234 * then pmtab->p_termtype retains its default value
235 * of "". otherwise it is set to a term type value
236 * that was returned.
237 */
238 if (strcmp(pmtab->p_device, "/dev/console") == 0 &&
239 (cn_fd = open("/dev/console", O_RDONLY)) != -1) {
240
241 if (strlen(pmtab->p_termtype) == 0 &&
242 ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) {
243 pmtab->p_termtype = cnterm.cn_term_type;
244 }
245
246 if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1)
247 pmtab->p_ttymode =
248 get_ttymode_prop(cnd.cnd_rconsdev);
249 (void) close(cn_fd);
250 }
251 }
252
253 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0'))
254 getty_account(pmtab->p_device); /* utmp accounting */
255 return (0);
256 }
257
258
259 /*
260 * ttymon_options - scan and check args for ttymon express
261 */
262
263 static void
ttymon_options(int argc,char ** argv,struct pmtab * pmtab)264 ttymon_options(int argc, char **argv, struct pmtab *pmtab)
265 {
266 int c; /* option letter */
267 char *timeout;
268 int gflag = 0; /* -g seen */
269 int size = 0;
270 char tbuf[BUFSIZ];
271
272 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) {
273 switch (c) {
274 case 'g':
275 gflag = 1;
276 break;
277 case 'd':
278 pmtab->p_device = optarg;
279 break;
280 case 'h':
281 pmtab->p_ttyflags &= ~H_FLAG;
282 break;
283
284 case 'T':
285 pmtab->p_termtype = optarg;
286 break;
287 /*
288 * case 'b':
289 * pmtab->p_ttyflags |= B_FLAG;
290 * pmtab->p_ttyflags |= R_FLAG;
291 * break;
292 */
293 case 't':
294 timeout = optarg;
295 while (*optarg) {
296 if (!isdigit(*optarg++)) {
297 log("Invalid argument for "
298 "\"-t\" -- number expected.");
299 usage();
300 }
301 }
302 pmtab->p_timeout = atoi(timeout);
303 break;
304 case 'p':
305 copystr(tbuf, optarg);
306 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE));
307 break;
308 case 'm':
309 pmtab->p_modules = optarg;
310 if (vml(pmtab->p_modules) != 0)
311 usage();
312 break;
313 case 'l':
314 pmtab->p_ttylabel = optarg;
315 break;
316 case '?':
317 usage();
318 break; /*NOTREACHED*/
319 }
320 }
321 if (optind < argc)
322 usage();
323
324 if (!gflag)
325 usage();
326 }
327
328 /*
329 * usage - print out a usage message
330 */
331
332 static void
usage(void)333 usage(void)
334 {
335 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] "
336 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n";
337
338 if (isatty(STDERR_FILENO))
339 (void) fprintf(stderr, "%s", umsg);
340 else
341 cons_printf(umsg);
342 exit(1);
343 }
344
345 /*
346 * getty_options - this is cut from getty.c
347 * - it scan getty cmd args
348 * - modification is made to stuff args in pmtab
349 */
350 static void
getty_options(int argc,char ** argv,struct pmtab * pmtab)351 getty_options(int argc, char **argv, struct pmtab *pmtab)
352 {
353 char *ptr;
354
355 /*
356 * the pre-4.0 getty's hang_up_line() is a no-op.
357 * For compatibility, H_FLAG cannot be set for this "getty".
358 */
359 pmtab->p_ttyflags &= ~(H_FLAG);
360
361 while (--argc && **++argv == '-') {
362 for (ptr = *argv + 1; *ptr; ptr++) {
363 switch (*ptr) {
364 case 'h':
365 break;
366 case 't':
367 if (isdigit(*++ptr)) {
368 (void) sscanf(ptr, "%d",
369 &(pmtab->p_timeout));
370 while (isdigit(*++ptr))
371 ;
372 ptr--;
373 } else if (--argc) {
374 if (isdigit(*(ptr = *++argv)))
375 (void) sscanf(ptr, "%d",
376 &(pmtab->p_timeout));
377 else {
378 log("getty: timeout argument "
379 "<%s> invalid", *argv);
380 exit(1);
381 }
382 }
383 break;
384
385 case 'c':
386 log("Use \"sttydefs -l\" to check "
387 "/etc/ttydefs.");
388 exit(0);
389 default:
390 break;
391 }
392 }
393 }
394
395 if (argc < 1) {
396 log("getty: no terminal line specified.");
397 exit(1);
398 } else {
399 (void) strcat(devbuf, "/dev/");
400 (void) strcat(devbuf, *argv);
401 pmtab->p_device = devbuf;
402 }
403
404 if (--argc > 0) {
405 pmtab->p_ttylabel = *++argv;
406 }
407
408 /*
409 * every thing after this will be ignored
410 * i.e. termtype and linedisc are ignored
411 */
412 }
413
414 /*
415 * find_ttyname(fd) - find the name of device associated with fd.
416 * - it first tries utmpx to see if an entry exists
417 * - with my pid and ut_line is defined. If ut_line
418 * - is defined, it will see if the major and minor
419 * - number of fd and devname from utmpx match.
420 * - If utmpx search fails, ttyname(fd) will be called.
421 */
422 static char *
find_ttyname(int fd)423 find_ttyname(int fd)
424 {
425 pid_t ownpid;
426 struct utmpx *u;
427 static struct stat statf, statu;
428 static char buf[BUFSIZ];
429
430 ownpid = getpid();
431 setutxent();
432 while ((u = getutxent()) != NULL) {
433 if (u->ut_pid == ownpid) {
434 if (strlen(u->ut_line) != 0) {
435 if (*(u->ut_line) != '/') {
436 (void) strcpy(buf, "/dev/");
437 (void) strncat(buf, u->ut_line,
438 sizeof (u->ut_line));
439 } else {
440 (void) strncat(buf, u->ut_line,
441 sizeof (u->ut_line));
442 }
443 }
444 else
445 u = NULL;
446 break;
447 }
448 }
449 endutxent();
450 if ((u != NULL) &&
451 (fstat(fd, &statf) == 0) &&
452 (stat(buf, &statu) == 0) &&
453 (statf.st_dev == statu.st_dev) &&
454 (statf.st_rdev == statu.st_rdev)) {
455 #ifdef DEBUG
456 debug("ttymon_express: find device name from utmpx.");
457 #endif
458 return (buf);
459 } else {
460 #ifdef DEBUG
461 debug("ttymon_express: calling ttyname to find device name.");
462 #endif
463 return (ttyname(fd));
464 }
465 }
466
467 /*
468 * Revoke all access to a device node and make sure that there are
469 * no interposed streams devices attached. Must be called before a
470 * device is actually opened.
471 * When fdetach is called, the underlying device node is revealed; it
472 * will have the previous owner and that owner can re-attach; so we
473 * retry until we win.
474 * Ignore non-existent devices.
475 */
476 void
revokedevaccess(char * dev,uid_t uid,gid_t gid,mode_t mode)477 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode)
478 {
479 do {
480 if (chown(dev, uid, gid) == -1)
481 return;
482 } while (fdetach(dev) == 0);
483
484 /* Remove ACLs */
485
486 (void) acl_strip(dev, uid, gid, mode);
487 }
488