sessreg.c revision 8b8e729c
1/* 2 * Copyright 1990, 1998 The Open Group 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that 7 * copyright notice and this permission notice appear in supporting 8 * documentation. 9 * 10 * The above copyright notice and this permission notice shall be included 11 * in all copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 15 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 16 * IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 17 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 18 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 19 * OTHER DEALINGS IN THE SOFTWARE. 20 * 21 * Except as contained in this notice, the name of The Open Group shall 22 * not be used in advertising or otherwise to promote the sale, use or 23 * other dealings in this Software without prior written authorization 24 * from The Open Group. 25 * 26 */ 27 28/* 29 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. 30 * 31 * Permission is hereby granted, free of charge, to any person obtaining a 32 * copy of this software and associated documentation files (the "Software"), 33 * to deal in the Software without restriction, including without limitation 34 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 35 * and/or sell copies of the Software, and to permit persons to whom the 36 * Software is furnished to do so, subject to the following conditions: 37 * 38 * The above copyright notice and this permission notice (including the next 39 * paragraph) shall be included in all copies or substantial portions of the 40 * Software. 41 * 42 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 45 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 47 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 48 * DEALINGS IN THE SOFTWARE. 49 */ 50 51/* 52 * Author: Keith Packard, MIT X Consortium 53 * Lastlog support and dynamic utmp entry allocation 54 * by Andreas Stolcke <stolcke@icsi.berkeley.edu> 55 */ 56 57/* 58 * sessreg 59 * 60 * simple wtmp/utmp frobber 61 * 62 * usage: sessreg [ -w <wtmp-file> ] [ -u <utmp-file> ] 63 * [ -l <line> ] 64 * [ -L <lastlog-file> ] / #ifdef USE_LASTLOG 65 * [ -h <host-name> ] / BSD only 66 * [ -s <slot-number> ] [ -x Xservers-file ] / BSD only 67 * [ -t <ttys-file> ] / BSD only 68 * [ -a ] [ -d ] user-name 69 * 70 * one of -a or -d must be specified 71 */ 72 73#include "sessreg.h" 74 75#include <X11/Xos.h> 76#include <X11/Xfuncs.h> 77#include <X11/Xfuncproto.h> 78#include <stdio.h> 79#include <stdlib.h> 80#include <time.h> 81 82#ifdef USE_UTMP 83static void set_utmp (struct utmp *u, char *line, char *user, char *host, 84 time_t date, int addp); 85#endif 86 87#ifdef USE_UTMPX 88static void set_utmpx (struct utmpx *u, const char *line, const char *user, 89 const char *host, time_t date, int addp); 90#endif 91 92static int wflag, uflag, lflag; 93static const char *wtmp_file, *utmp_file; 94#ifdef USE_UTMPX 95#ifdef HAVE_UPDWTMPX 96static const char *wtmpx_file = NULL; 97#endif 98#ifdef HAVE_UTMPXNAME 99static const char *utmpx_file = NULL; 100#endif 101#endif 102 103static int utmp_none, wtmp_none; 104/* 105 * BSD specific variables. To make life much easier for Xstartup/Xreset 106 * maintainers, these arguments are accepted but ignored for sysV 107 */ 108static int hflag, xflag, tflag; 109static char *host_name = NULL; 110#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 111static int sflag; 112static int slot_number; 113#endif 114static char *xservers_file, *ttys_file; 115static char *user_name; 116static int aflag, dflag; 117#ifdef USE_LASTLOG 118static const char *llog_file; 119static int llog_none, Lflag; 120#endif 121 122static char *program_name; 123 124#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 125static int findslot (char *line_name, char *host_name, int addp, int slot); 126static int Xslot (char *ttys_file, char *servers_file, char *tty_line, 127 char *host_name, int addp); 128#endif 129 130static void _X_NORETURN _X_COLD 131usage (int x) 132{ 133 fprintf (stderr, 134 "%s: usage %s {-a -d} [-w wtmp-file] [-u utmp-file]" 135#ifdef USE_LASTLOG 136 " [-L lastlog-file]" 137#endif 138 "\n" 139 " [-t ttys-file] [-l line-name] [-h host-name] [-V]\n" 140 " [-s slot-number] [-x servers-file] user-name\n", 141 program_name, program_name); 142 exit (x); 143} 144 145static char * 146getstring (char ***avp, int *flagp) 147{ 148 char **a = *avp; 149 char *flag = *a; 150 151 if (*flagp != 0) { 152 fprintf (stderr, "%s: cannot give more than one -%s option\n", 153 program_name, flag); 154 usage (1); 155 } 156 *flagp = 1; 157 /* if the argument is given immediately following the flag, 158 i.e. "sessreg -hfoo ...", not "sessreg -h foo ...", 159 then return the rest of the string as the argument value */ 160 if (*++*a) 161 return *a; 162 /* else use the next pointer in the argv list as the argument value */ 163 ++a; 164 if (!*a) { 165 fprintf (stderr, "%s: -%s requires an argument\n", 166 program_name, flag); 167 usage (1); 168 } 169 *avp = a; 170 return *a; 171} 172 173#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 174static int 175syserr (int x, const char *s) 176{ 177 if (x == -1) { 178 perror (s); 179 exit (1); 180 } 181 return x; 182} 183#endif 184 185static int 186sysnerr (int x, const char *s) 187{ 188 if (x == 0) { 189 perror (s); 190 exit (1); 191 } 192 return x; 193} 194 195/* 196 * While this looks like it could be replaced with strlcpy() on platforms 197 * that have it, we're sticking with strncpy() so that we zero out the 198 * whole buffer to avoid writing garbage to the fixed length fields in the 199 * utmp/wtmp files, since strlcpy() does not write past the \0 terminator. 200 */ 201static void 202safe_strncpy(char *dest, const char *src, size_t n) 203{ 204 (void)strncpy(dest, src, n); 205 if (n > 0) 206 dest[n - 1] = '\0'; 207} 208 209int 210main (int argc, char **argv) 211{ 212#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 213 int utmp; 214#endif 215#ifndef USE_UTMPX 216 int wtmp; 217#endif 218 time_t current_time; 219#ifdef USE_UTMP 220 struct utmp utmp_entry; 221#endif 222#ifdef USE_UTMPX 223 struct utmpx utmpx_entry; 224#endif 225 char * line = NULL; 226 227 program_name = argv[0]; 228 while (*++argv && **argv == '-') { 229 switch (*++*argv) { 230 case 'w': 231 wtmp_file = getstring (&argv, &wflag); 232 if (!strcmp (wtmp_file, "none")) 233 wtmp_none = 1; 234#if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX) 235 else 236 wtmpx_file = wtmp_file; 237#endif 238 break; 239 case 'u': 240 utmp_file = getstring (&argv, &uflag); 241 if (!strcmp (utmp_file, "none")) 242 utmp_none = 1; 243#if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME) 244 else 245 utmpx_file = utmp_file; 246#endif 247 break; 248#ifdef USE_LASTLOG 249 case 'L': 250 llog_file = getstring (&argv, &Lflag); 251 if (!strcmp (llog_file, "none")) 252 llog_none = 1; 253 break; 254#endif 255 case 't': 256 ttys_file = getstring (&argv, &tflag); 257 break; 258 case 'l': 259 line = getstring (&argv, &lflag); 260 break; 261 case 'h': 262 host_name = getstring (&argv, &hflag); 263 break; 264 case 's': 265#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 266 slot_number = atoi (getstring (&argv, &sflag)); 267#endif 268 break; 269 case 'x': 270 xservers_file = getstring (&argv, &xflag); 271 break; 272 case 'a': 273 aflag++; 274 break; 275 case 'd': 276 dflag++; 277 break; 278 case 'V': 279 printf("%s\n", PACKAGE_STRING); 280 exit (0); 281 default: 282 fprintf (stderr, "%s: unrecognized option '%s'\n", 283 program_name, argv[0]); 284 usage (1); 285 } 286 } 287 user_name = *argv++; 288 if (user_name == NULL) { 289 fprintf (stderr, "%s: missing required user-name argument\n", 290 program_name); 291 usage (1); 292 } 293 if (*argv != NULL) { 294 fprintf (stderr, "%s: unrecognized argument '%s'\n", 295 program_name, argv[0]); 296 usage (1); 297 } 298 /* 299 * complain if neither aflag nor dflag are set, 300 * or if both are set. 301 */ 302 if (!(aflag ^ dflag)) { 303 fprintf (stderr, "%s: must specify exactly one of -a or -d\n", 304 program_name); 305 usage (1); 306 } 307 if (xflag && !lflag) { 308 fprintf (stderr, "%s: must specify -l when -x is used\n", 309 program_name); 310 usage (1); 311 } 312 /* set up default file names */ 313 if (!wflag) { 314 wtmp_file = WTMP_FILE; 315#if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX) 316 wtmpx_file = WTMPX_FILE; 317#endif 318 } 319 if (!uflag) { 320 utmp_file = UTMP_FILE; 321#if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME) 322 utmpx_file = UTMPX_FILE; 323#endif 324 } 325#ifdef USE_LASTLOG 326 if (!Lflag) 327 llog_file = LLOG_FILE; 328#endif 329#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 330 if (!tflag) 331 ttys_file = TTYS_FILE; 332 if (!sflag && !utmp_none) { 333 if (xflag) 334 sysnerr (slot_number = Xslot (ttys_file, xservers_file, line, host_name, aflag), "Xslot"); 335 else 336 sysnerr (slot_number = ttyslot (), "ttyslot"); 337 } 338#endif 339 if (!lflag) { 340 sysnerr ((line = ttyname (0)) != NULL, "ttyname"); 341 if (strncmp(line, "/dev/", 5) == 0) 342 line += 5; 343 } 344 time (¤t_time); 345#ifdef USE_UTMP 346 set_utmp (&utmp_entry, line, user_name, host_name, current_time, aflag); 347#endif 348 349#ifdef USE_UTMPX 350 /* need to set utmpxname() before calling set_utmpx() for 351 UtmpxIdOpen to work */ 352# ifdef HAVE_UTMPXNAME 353 if (utmpx_file != NULL) { 354 utmpxname (utmpx_file); 355 } 356# endif 357 set_utmpx (&utmpx_entry, line, user_name, 358 host_name, current_time, aflag); 359#endif 360 361 if (!utmp_none) { 362#ifdef USE_UTMPX 363# ifdef HAVE_UTMPXNAME 364 if (utmpx_file != NULL) 365# endif 366 { 367 setutxent (); 368 (void) getutxid (&utmpx_entry); 369 pututxline (&utmpx_entry); 370 endutxent (); 371 } 372#endif 373#ifdef USE_UTMP 374# ifdef HAVE_PUTUTLINE 375 utmpname (utmp_file); 376 setutent (); 377 (void) getutid (&utmp_entry); 378 pututline (&utmp_entry); 379 endutent (); 380# else 381 utmp = open (utmp_file, O_RDWR); 382 if (utmp != -1) { 383 syserr ((int) lseek (utmp, (off_t) slot_number * sizeof (struct utmp), 0), "lseek"); 384 sysnerr (write (utmp, (char *) &utmp_entry, sizeof (utmp_entry)) 385 == sizeof (utmp_entry), "write utmp entry"); 386 close (utmp); 387 } 388# endif 389#endif /* USE_UTMP */ 390 } 391 if (!wtmp_none) { 392#ifdef USE_UTMPX 393# ifdef HAVE_UPDWTMPX 394 if (wtmpx_file != NULL) { 395 updwtmpx(wtmpx_file, &utmpx_entry); 396 } 397# endif 398#else 399 wtmp = open (wtmp_file, O_WRONLY|O_APPEND); 400 if (wtmp != -1) { 401 sysnerr (write (wtmp, (char *) &utmp_entry, sizeof (utmp_entry)) 402 == sizeof (utmp_entry), "write wtmp entry"); 403 close (wtmp); 404 } 405#endif 406 } 407#ifdef USE_LASTLOG 408 if (aflag && !llog_none) { 409 int llog; 410 struct passwd *pwd = getpwnam(user_name); 411 412 sysnerr( pwd != NULL, "get user id"); 413 llog = open (llog_file, O_RDWR); 414 415 if (llog != -1) { 416 struct lastlog ll; 417 418 sysnerr (lseek(llog, (off_t) (pwd->pw_uid*sizeof(ll)), 0) 419 != -1, "seeking lastlog entry"); 420 memset(&ll, 0, sizeof(ll)); 421 ll.ll_time = current_time; 422 if (line) 423 safe_strncpy (ll.ll_line, line, sizeof (ll.ll_line)); 424 if (host_name) 425 safe_strncpy (ll.ll_host, host_name, sizeof (ll.ll_host)); 426 427 sysnerr (write (llog, (char *) &ll, sizeof (ll)) 428 == sizeof (ll), "write lastlog entry"); 429 close (llog); 430 } 431 } 432#endif 433 return 0; 434} 435 436/* 437 * fill in the appropriate records of the utmp entry 438 */ 439 440#ifdef USE_UTMP 441static void 442set_utmp (struct utmp *u, char *line, char *user, char *host, time_t date, int addp) 443{ 444 memset (u, 0, sizeof (*u)); 445 if (line) 446 safe_strncpy (u->ut_line, line, sizeof (u->ut_line)); 447 else 448 memset (u->ut_line, 0, sizeof (u->ut_line)); 449 if (addp && user) 450 safe_strncpy (u->ut_name, user, sizeof (u->ut_name)); 451 else 452 memset (u->ut_name, 0, sizeof (u->ut_name)); 453#ifdef HAVE_STRUCT_UTMP_UT_ID 454 if (line) { 455 size_t i; 456 /* 457 * this is a bit crufty, but 458 * follows the apparent conventions in 459 * the ttys file. ut_id is only 4 bytes 460 * long, and the last 4 bytes of the line 461 * name are written into it, left justified. 462 */ 463 i = strlen (line); 464 if (i >= sizeof (u->ut_id)) 465 i -= sizeof (u->ut_id); 466 else 467 i = 0; 468 safe_strncpy (u->ut_id, line + i, sizeof (u->ut_id)); 469 } else 470 memset (u->ut_id, 0, sizeof (u->ut_id)); 471#endif 472#ifdef HAVE_STRUCT_UTMP_UT_PID 473 if (addp) 474 u->ut_pid = getppid (); 475 else 476 u->ut_pid = 0; 477#endif 478#ifdef HAVE_STRUCT_UTMP_UT_TYPE 479 if (addp) 480 u->ut_type = USER_PROCESS; 481 else 482 u->ut_type = DEAD_PROCESS; 483#endif 484#ifdef HAVE_STRUCT_UTMP_UT_HOST 485 if (addp && host) 486 safe_strncpy (u->ut_host, host, sizeof (u->ut_host)); 487 else 488 memset (u->ut_host, 0, sizeof (u->ut_host)); 489#endif 490 u->ut_time = date; 491} 492#endif /* USE_UTMP */ 493 494#ifdef USE_UTMPX 495static int 496UtmpxIdOpen( char *utmpId ) 497{ 498 struct utmpx *u; /* pointer to entry in utmp file */ 499 int status = 1; /* return code */ 500 501 setutxent(); 502 503 while ( (u = getutxent()) != NULL ) { 504 505 if ( (strncmp(u->ut_id, utmpId, 4) == 0 ) && 506 u->ut_type != DEAD_PROCESS ) { 507 508 status = 0; 509 break; 510 } 511 } 512 513 endutxent(); 514 return (status); 515} 516 517static void 518set_utmpx (struct utmpx *u, const char *line, const char *user, 519 const char *host, time_t date, int addp) 520{ 521 static const char letters[] = 522 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 523 524 memset (u, 0, sizeof (*u)); 525 if (line) 526 { 527 if(strcmp(line, ":0") == 0) 528 (void) strcpy(u->ut_line, "console"); 529 else 530 safe_strncpy (u->ut_line, line, sizeof (u->ut_line)); 531 532 safe_strncpy(u->ut_host, line, sizeof(u->ut_host)); 533#ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN 534 u->ut_syslen = strlen(line); 535#endif 536 } 537 else 538 memset (u->ut_line, 0, sizeof (u->ut_line)); 539 if (addp && user) 540 safe_strncpy (u->ut_user, user, sizeof (u->ut_user)); 541 else 542 memset (u->ut_user, 0, sizeof (u->ut_user)); 543 544 if (line) { 545 size_t i; 546 /* 547 * this is a bit crufty, but 548 * follows the apparent conventions in 549 * the ttys file. ut_id is only 4 bytes 550 * long, and the last 4 bytes of the line 551 * name are written into it, left justified. 552 */ 553 i = strlen (line); 554 if (i >= sizeof (u->ut_id)) 555 i -= sizeof (u->ut_id); 556 else 557 i = 0; 558 safe_strncpy (u->ut_id, line + i, sizeof (u->ut_id)); 559 560 /* make sure there is no entry using identical ut_id */ 561 if (!UtmpxIdOpen(u->ut_id) && addp) { 562 int limit = sizeof(letters) - 1; 563 int t = 0; 564 565 u->ut_id[1] = line[i]; 566 u->ut_id[2] = line[i+1]; 567 u->ut_id[3] = line[i+2]; 568 do { 569 u->ut_id[0] = letters[t]; 570 t++; 571 } while (!UtmpxIdOpen(u->ut_id) && (t < limit)); 572 } 573 if (!addp && strstr(line, ":") != NULL) { 574 struct utmpx *tmpu; 575 576 while ( (tmpu = getutxent()) != NULL ) { 577 if ( (strcmp(tmpu->ut_host, line) == 0 ) && 578 tmpu->ut_type != DEAD_PROCESS ) { 579 strncpy(u->ut_id, tmpu->ut_id, 580 sizeof(u->ut_id)); 581 break; 582 } 583 } 584 endutxent(); 585 } 586 } else 587 memset (u->ut_id, 0, sizeof (u->ut_id)); 588 589 if (addp) { 590 u->ut_pid = getppid (); 591 u->ut_type = USER_PROCESS; 592 } else { 593 u->ut_pid = 0; 594 u->ut_type = DEAD_PROCESS; 595 } 596 u->ut_tv.tv_sec = date; 597 u->ut_tv.tv_usec = 0; 598} 599#endif /* USE_UTMPX */ 600 601#if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE) 602/* 603 * compute the slot-number for an X display. This is computed 604 * by counting the lines in /etc/ttys and adding the line-number 605 * that the display appears on in Xservers. This is a poor 606 * design, but is limited by the non-existant interface to utmp. 607 * If host_name is non-NULL, assume it contains the display name, 608 * otherwise use the tty_line argument (i.e., the tty name). 609 */ 610 611static int 612Xslot (char *ttys_file, char *servers_file, char *tty_line, char *host_name, 613 int addp) 614{ 615 FILE *ttys, *servers; 616 int c; 617 int slot = 1; 618 int column0 = 1; 619 char servers_line[1024]; 620 char disp_name[512]; 621 int len; 622 char *pos; 623 624 /* remove screen number from the display name */ 625 memset(disp_name, 0, sizeof(disp_name)); 626 strncpy(disp_name, host_name ? host_name : tty_line, sizeof(disp_name)-1); 627 pos = strrchr(disp_name, ':'); 628 if (pos) { 629 pos = strchr(pos, '.'); 630 if (pos) 631 *pos = '\0'; 632 } 633 sysnerr ((int)(long)(ttys = fopen (ttys_file, "r")), ttys_file); 634 while ((c = getc (ttys)) != EOF) 635 if (c == '\n') { 636 ++slot; 637 column0 = 1; 638 } else 639 column0 = 0; 640 if (!column0) 641 ++slot; 642 (void) fclose (ttys); 643 sysnerr ((int)(long)(servers = fopen (servers_file, "r")), servers_file); 644 645 len = strlen (disp_name); 646 column0 = 1; 647 while (fgets (servers_line, sizeof (servers_line), servers)) { 648 if (column0 && *servers_line != '#') { 649 if (!strncmp (disp_name, servers_line, len) && 650 (servers_line[len] == ' ' || 651 servers_line[len] == '\t')) 652 return slot; 653 ++slot; 654 } 655 if (servers_line[strlen(servers_line)-1] != '\n') 656 column0 = 0; 657 else 658 column0 = 1; 659 } 660 /* 661 * display not found in Xservers file - allocate utmp entry dinamically 662 */ 663 return findslot (tty_line, host_name, addp, slot); 664} 665 666/* 667 * find a free utmp slot for the X display. This allocates a new entry 668 * past the regular tty entries if necessary, reusing existing entries 669 * (identified by (line,hostname)) if possible. 670 */ 671static int 672findslot (char *line_name, char *host_name, int addp, int slot) 673{ 674 int utmp; 675 struct utmp entry; 676 int found = 0; 677 int freeslot = -1; 678 679 syserr(utmp = open (utmp_file, O_RDONLY), "open utmp"); 680 681 /* 682 * first, try to locate a previous entry for this display 683 * also record location of a free slots in case we need a new one 684 */ 685 syserr ((int) lseek (utmp, (off_t) slot * sizeof (struct utmp), 0), "lseek"); 686 687 if (!host_name) 688 host_name = ""; 689 690 while (read (utmp, (char *) &entry, sizeof (entry)) == sizeof (entry)) { 691 if (strncmp(entry.ut_line, line_name, 692 sizeof(entry.ut_line)) == 0 693#ifdef HAVE_STRUCT_UTMP_UT_HOST 694 && 695 strncmp(entry.ut_host, host_name, 696 sizeof(entry.ut_host)) == 0 697#endif 698 ) { 699 found = 1; 700 break; 701 } 702 if (freeslot < 0 && *entry.ut_name == '\0') 703 freeslot = slot; 704 ++slot; 705 } 706 707 close (utmp); 708 709 if (found) 710 return slot; 711 else if (!addp) 712 return 0; /* trying to delete a non-existing entry */ 713 else if (freeslot < 0) 714 return slot; /* first slot past current entries */ 715 else 716 return freeslot; 717} 718#endif 719