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