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 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 * 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 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. 228 * 229 * If -d /dev/console also specified, we send an ioctl 230 * to the console device to query the TERM type. 231 * 232 * If any of the tests, system calls, or ioctls fail 233 * then pmtab->p_termtype retains its default value 234 * of "". otherwise it is set to a term type value 235 * that was returned. 236 */ 237 if ((strlen(pmtab->p_termtype) == 0) && 238 (strcmp(pmtab->p_device, "/dev/console") == 0) && 239 ((cn_fd = open("/dev/console", O_RDONLY)) != -1)) { 240 241 if (ioctl(cn_fd, CONS_GETTERM, &cnterm) != -1) 242 pmtab->p_termtype = cnterm.cn_term_type; 243 244 if (ioctl(cn_fd, CONS_GETDEV, &cnd) != -1) 245 pmtab->p_ttymode = 246 get_ttymode_prop(cnd.cnd_rconsdev); 247 (void) close(cn_fd); 248 } 249 } 250 251 if ((pmtab->p_device != NULL) && (*(pmtab->p_device) != '\0')) 252 getty_account(pmtab->p_device); /* utmp accounting */ 253 return (0); 254 } 255 256 257 /* 258 * ttymon_options - scan and check args for ttymon express 259 */ 260 261 static void 262 ttymon_options(int argc, char **argv, struct pmtab *pmtab) 263 { 264 int c; /* option letter */ 265 char *timeout; 266 int gflag = 0; /* -g seen */ 267 int size = 0; 268 char tbuf[BUFSIZ]; 269 270 while ((c = getopt(argc, argv, "T:gd:ht:p:m:l:")) != -1) { 271 switch (c) { 272 case 'g': 273 gflag = 1; 274 break; 275 case 'd': 276 pmtab->p_device = optarg; 277 break; 278 case 'h': 279 pmtab->p_ttyflags &= ~H_FLAG; 280 break; 281 282 case 'T': 283 pmtab->p_termtype = optarg; 284 break; 285 /* 286 * case 'b': 287 * pmtab->p_ttyflags |= B_FLAG; 288 * pmtab->p_ttyflags |= R_FLAG; 289 * break; 290 */ 291 case 't': 292 timeout = optarg; 293 while (*optarg) { 294 if (!isdigit(*optarg++)) { 295 log("Invalid argument for " 296 "\"-t\" -- number expected."); 297 usage(); 298 } 299 } 300 pmtab->p_timeout = atoi(timeout); 301 break; 302 case 'p': 303 copystr(tbuf, optarg); 304 pmtab->p_prompt = strsave(getword(tbuf, &size, TRUE)); 305 break; 306 case 'm': 307 pmtab->p_modules = optarg; 308 if (vml(pmtab->p_modules) != 0) 309 usage(); 310 break; 311 case 'l': 312 pmtab->p_ttylabel = optarg; 313 break; 314 case '?': 315 usage(); 316 break; /*NOTREACHED*/ 317 } 318 } 319 if (optind < argc) 320 usage(); 321 322 if (!gflag) 323 usage(); 324 } 325 326 /* 327 * usage - print out a usage message 328 */ 329 330 static void 331 usage(void) 332 { 333 char *umsg = "Usage: ttymon\n ttymon -g [-h] [-d device] " 334 "[-l ttylabel] [-t timeout] [-p prompt] [-m modules]\n"; 335 336 if (isatty(STDERR_FILENO)) 337 (void) fprintf(stderr, "%s", umsg); 338 else 339 cons_printf(umsg); 340 exit(1); 341 } 342 343 /* 344 * getty_options - this is cut from getty.c 345 * - it scan getty cmd args 346 * - modification is made to stuff args in pmtab 347 */ 348 static void 349 getty_options(int argc, char **argv, struct pmtab *pmtab) 350 { 351 char *ptr; 352 353 /* 354 * the pre-4.0 getty's hang_up_line() is a no-op. 355 * For compatibility, H_FLAG cannot be set for this "getty". 356 */ 357 pmtab->p_ttyflags &= ~(H_FLAG); 358 359 while (--argc && **++argv == '-') { 360 for (ptr = *argv + 1; *ptr; ptr++) { 361 switch (*ptr) { 362 case 'h': 363 break; 364 case 't': 365 if (isdigit(*++ptr)) { 366 (void) sscanf(ptr, "%d", 367 &(pmtab->p_timeout)); 368 while (isdigit(*++ptr)) 369 ; 370 ptr--; 371 } else if (--argc) { 372 if (isdigit(*(ptr = *++argv))) 373 (void) sscanf(ptr, "%d", 374 &(pmtab->p_timeout)); 375 else { 376 log("getty: timeout argument " 377 "<%s> invalid", *argv); 378 exit(1); 379 } 380 } 381 break; 382 383 case 'c': 384 log("Use \"sttydefs -l\" to check " 385 "/etc/ttydefs."); 386 exit(0); 387 default: 388 break; 389 } 390 } 391 } 392 393 if (argc < 1) { 394 log("getty: no terminal line specified."); 395 exit(1); 396 } else { 397 (void) strcat(devbuf, "/dev/"); 398 (void) strcat(devbuf, *argv); 399 pmtab->p_device = devbuf; 400 } 401 402 if (--argc > 0) { 403 pmtab->p_ttylabel = *++argv; 404 } 405 406 /* 407 * every thing after this will be ignored 408 * i.e. termtype and linedisc are ignored 409 */ 410 } 411 412 /* 413 * find_ttyname(fd) - find the name of device associated with fd. 414 * - it first tries utmpx to see if an entry exists 415 * - with my pid and ut_line is defined. If ut_line 416 * - is defined, it will see if the major and minor 417 * - number of fd and devname from utmpx match. 418 * - If utmpx search fails, ttyname(fd) will be called. 419 */ 420 static char * 421 find_ttyname(int fd) 422 { 423 pid_t ownpid; 424 struct utmpx *u; 425 static struct stat statf, statu; 426 static char buf[BUFSIZ]; 427 428 ownpid = getpid(); 429 setutxent(); 430 while ((u = getutxent()) != NULL) { 431 if (u->ut_pid == ownpid) { 432 if (strlen(u->ut_line) != 0) { 433 if (*(u->ut_line) != '/') { 434 (void) strcpy(buf, "/dev/"); 435 (void) strncat(buf, u->ut_line, 436 sizeof (u->ut_line)); 437 } else { 438 (void) strncat(buf, u->ut_line, 439 sizeof (u->ut_line)); 440 } 441 } 442 else 443 u = NULL; 444 break; 445 } 446 } 447 endutxent(); 448 if ((u != NULL) && 449 (fstat(fd, &statf) == 0) && 450 (stat(buf, &statu) == 0) && 451 (statf.st_dev == statu.st_dev) && 452 (statf.st_rdev == statu.st_rdev)) { 453 #ifdef DEBUG 454 debug("ttymon_express: find device name from utmpx."); 455 #endif 456 return (buf); 457 } else { 458 #ifdef DEBUG 459 debug("ttymon_express: calling ttyname to find device name."); 460 #endif 461 return (ttyname(fd)); 462 } 463 } 464 465 /* 466 * Revoke all access to a device node and make sure that there are 467 * no interposed streams devices attached. Must be called before a 468 * device is actually opened. 469 * When fdetach is called, the underlying device node is revealed; it 470 * will have the previous owner and that owner can re-attach; so we 471 * retry until we win. 472 * Ignore non-existent devices. 473 */ 474 void 475 revokedevaccess(char *dev, uid_t uid, gid_t gid, mode_t mode) 476 { 477 do { 478 if (chown(dev, uid, gid) == -1) 479 return; 480 } while (fdetach(dev) == 0); 481 482 /* Remove ACLs */ 483 484 (void) acl_strip(dev, uid, gid, mode); 485 } 486