2 * Copyright 1990, 1998 The Open Group
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
10 * The above copyright notice and this permission notice shall be included
11 * in all copies or substantial portions of the Software.
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.
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.
29 * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
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:
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
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.
52 * Author: Keith Packard, MIT X Consortium
53 * Lastlog support and dynamic utmp entry allocation
54 * by Andreas Stolcke <stolcke@icsi.berkeley.edu>
60 * simple wtmp/utmp frobber
62 * usage: sessreg [ -w <wtmp-file> ] [ -u <utmp-file> ]
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
70 * one of -a or -d must be specified
76 #include <X11/Xfuncs.h>
82 static void set_utmp (struct utmp *u, char *line, char *user, char *host,
83 time_t date, int addp);
87 static void set_utmpx (struct utmpx *u, const char *line, const char *user,
88 const char *host, time_t date, int addp);
91 static int wflag, uflag, lflag;
92 static const char *wtmp_file, *utmp_file;
96 static const char *wtmpx_file = NULL;
99 static const char *utmpx_file = NULL;
102 static int utmp_none, wtmp_none;
104 * BSD specific variables. To make life much easier for Xstartup/Xreset
105 * maintainers, these arguments are accepted but ignored for sysV
107 static int hflag, xflag, tflag;
108 static char *host_name = NULL;
109 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
111 static int slot_number;
113 static char *xservers_file, *ttys_file;
114 static char *user_name;
115 static int aflag, dflag;
117 static const char *llog_file;
118 static int llog_none, Lflag;
121 static char *program_name;
123 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
124 static int findslot (char *line_name, char *host_name, int addp, int slot);
125 static int Xslot (char *ttys_file, char *servers_file, char *tty_line,
126 char *host_name, int addp);
133 fprintf (stderr, "%s: usage %s {-a -d} [-w wtmp-file] [-u utmp-file]", program_name, program_name);
135 fprintf (stderr, " [-L lastlog-file]");
137 fprintf (stderr, "\n");
138 fprintf (stderr, " [-t ttys-file] [-l line-name] [-h host-name]\n");
139 fprintf (stderr, " [-s slot-number] [-x servers-file] user-name\n");
146 getstring (char ***avp, int *flagp)
159 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
161 syserr (int x, const char *s)
172 sysnerr (int x, const char *s)
182 main (int argc, char **argv)
184 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
192 struct utmp utmp_entry;
195 struct utmpx utmpx_entry;
198 program_name = argv[0];
199 while (*++argv && **argv == '-') {
202 wtmp_file = getstring (&argv, &wflag);
203 if (!strcmp (wtmp_file, "none"))
207 utmp_file = getstring (&argv, &uflag);
208 if (!strcmp (utmp_file, "none"))
213 llog_file = getstring (&argv, &Lflag);
214 if (!strcmp (llog_file, "none"))
219 ttys_file = getstring (&argv, &tflag);
222 line = getstring (&argv, &lflag);
225 host_name = getstring (&argv, &hflag);
228 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
229 slot_number = atoi (getstring (&argv, &sflag));
233 xservers_file = getstring (&argv, &xflag);
245 usage (!(user_name = *argv++));
246 usage (*argv != NULL);
248 * complain if neither aflag nor dflag are set,
249 * or if both are set.
251 usage (!(aflag ^ dflag));
252 usage (xflag && !lflag);
253 /* set up default file names */
255 wtmp_file = WTMP_FILE;
256 #if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX)
257 wtmpx_file = WTMPX_FILE;
262 utmp_file = UTMP_FILE;
263 #if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME)
264 utmpx_file = UTMPX_FILE;
272 llog_file = LLOG_FILE;
274 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
276 ttys_file = TTYS_FILE;
277 if (!sflag && !utmp_none) {
279 sysnerr (slot_number = Xslot (ttys_file, xservers_file, line, host_name, aflag), "Xslot");
281 sysnerr (slot_number = ttyslot (), "ttyslot");
285 sysnerr ((line = ttyname (0)) != NULL, "ttyname");
286 if (strncmp(line, "/dev/", 5) == 0)
289 time (¤t_time);
291 set_utmp (&utmp_entry, line, user_name, host_name, current_time, aflag);
295 /* need to set utmpxname() before calling set_utmpx() for
296 UtmpxIdOpen to work */
297 # ifdef HAVE_UTMPXNAME
298 if (utmpx_file != NULL) {
299 utmpxname (utmpx_file);
302 set_utmpx (&utmpx_entry, line, user_name,
303 host_name, current_time, aflag);
308 # ifdef HAVE_UTMPXNAME
309 if (utmpx_file != NULL)
313 (void) getutxid (&utmpx_entry);
314 pututxline (&utmpx_entry);
319 # ifdef HAVE_PUTUTLINE
320 utmpname (utmp_file);
322 (void) getutid (&utmp_entry);
323 pututline (&utmp_entry);
326 utmp = open (utmp_file, O_RDWR);
328 syserr ((int) lseek (utmp, (long) slot_number * sizeof (struct utmp), 0), "lseek");
329 sysnerr (write (utmp, (char *) &utmp_entry, sizeof (utmp_entry))
330 == sizeof (utmp_entry), "write utmp entry");
334 #endif /* USE_UTMP */
338 # ifdef HAVE_UPDWTMPX
339 if (wtmpx_file != NULL) {
340 updwtmpx(wtmpx_file, &utmpx_entry);
344 wtmp = open (wtmp_file, O_WRONLY|O_APPEND);
346 sysnerr (write (wtmp, (char *) &utmp_entry, sizeof (utmp_entry))
347 == sizeof (utmp_entry), "write wtmp entry");
353 if (aflag && !llog_none) {
355 struct passwd *pwd = getpwnam(user_name);
357 sysnerr( pwd != NULL, "get user id");
358 llog = open (llog_file, O_RDWR);
363 sysnerr (lseek(llog, (long) pwd->pw_uid*sizeof(ll), 0)
364 != -1, "seeking lastlog entry");
365 memset(&ll, 0, sizeof(ll));
366 ll.ll_time = current_time;
368 (void) strncpy (ll.ll_line, line, sizeof (ll.ll_line));
370 (void) strncpy (ll.ll_host, host_name, sizeof (ll.ll_host));
372 sysnerr (write (llog, (char *) &ll, sizeof (ll))
373 == sizeof (ll), "write lastlog entry");
382 * fill in the appropriate records of the utmp entry
387 set_utmp (struct utmp *u, char *line, char *user, char *host, time_t date, int addp)
389 memset (u, 0, sizeof (*u));
391 (void) strncpy (u->ut_line, line, sizeof (u->ut_line));
393 memset (u->ut_line, 0, sizeof (u->ut_line));
395 (void) strncpy (u->ut_name, user, sizeof (u->ut_name));
397 memset (u->ut_name, 0, sizeof (u->ut_name));
398 #ifdef HAVE_STRUCT_UTMP_UT_ID
402 * this is a bit crufty, but
403 * follows the apparent conventions in
404 * the ttys file. ut_id is only 4 bytes
405 * long, and the last 4 bytes of the line
406 * name are written into it, left justified.
409 if (i >= sizeof (u->ut_id))
410 i -= sizeof (u->ut_id);
413 (void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
415 memset (u->ut_id, 0, sizeof (u->ut_id));
417 #ifdef HAVE_STRUCT_UTMP_UT_PID
419 u->ut_pid = getppid ();
423 #ifdef HAVE_STRUCT_UTMP_UT_TYPE
425 u->ut_type = USER_PROCESS;
427 u->ut_type = DEAD_PROCESS;
429 #ifdef HAVE_STRUCT_UTMP_UT_HOST
431 (void) strncpy (u->ut_host, host, sizeof (u->ut_host));
433 memset (u->ut_host, 0, sizeof (u->ut_host));
437 #endif /* USE_UTMP */
441 UtmpxIdOpen( char *utmpId )
443 struct utmpx *u; /* pointer to entry in utmp file */
444 int status = 1; /* return code */
448 while ( (u = getutxent()) != NULL ) {
450 if ( (strncmp(u->ut_id, utmpId, 4) == 0 ) &&
451 u->ut_type != DEAD_PROCESS ) {
463 set_utmpx (struct utmpx *u, const char *line, const char *user,
464 const char *host, time_t date, int addp)
466 static const char letters[] =
467 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
471 if(strcmp(line, ":0") == 0)
472 (void) strcpy(u->ut_line, "console");
474 (void) strncpy (u->ut_line, line, sizeof (u->ut_line));
476 strncpy(u->ut_host, line, sizeof(u->ut_host));
477 #ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
478 u->ut_syslen = strlen(line);
482 memset (u->ut_line, 0, sizeof (u->ut_line));
484 (void) strncpy (u->ut_user, user, sizeof (u->ut_user));
486 memset (u->ut_user, 0, sizeof (u->ut_user));
491 * this is a bit crufty, but
492 * follows the apparent conventions in
493 * the ttys file. ut_id is only 4 bytes
494 * long, and the last 4 bytes of the line
495 * name are written into it, left justified.
498 if (i >= sizeof (u->ut_id))
499 i -= sizeof (u->ut_id);
502 (void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
504 /* make sure there is no entry using identical ut_id */
505 if (!UtmpxIdOpen(u->ut_id) && addp) {
506 int limit = sizeof(letters) - 1;
509 u->ut_id[1] = line[i];
510 u->ut_id[2] = line[i+1];
511 u->ut_id[3] = line[i+2];
513 u->ut_id[0] = letters[t];
515 } while (!UtmpxIdOpen(u->ut_id) && (t < limit));
517 if (!addp && strstr(line, ":") != NULL) {
520 while ( (tmpu = getutxent()) != NULL ) {
521 if ( (strcmp(tmpu->ut_host, line) == 0 ) &&
522 tmpu->ut_type != DEAD_PROCESS ) {
523 strncpy(u->ut_id, tmpu->ut_id,
531 memset (u->ut_id, 0, sizeof (u->ut_id));
534 u->ut_pid = getppid ();
535 u->ut_type = USER_PROCESS;
538 u->ut_type = DEAD_PROCESS;
540 u->ut_tv.tv_sec = date;
541 u->ut_tv.tv_usec = 0;
543 #endif /* USE_UTMPX */
545 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
547 * compute the slot-number for an X display. This is computed
548 * by counting the lines in /etc/ttys and adding the line-number
549 * that the display appears on in Xservers. This is a poor
550 * design, but is limited by the non-existant interface to utmp.
551 * If host_name is non-NULL, assume it contains the display name,
552 * otherwise use the tty_line argument (i.e., the tty name).
556 Xslot (char *ttys_file, char *servers_file, char *tty_line, char *host_name,
559 FILE *ttys, *servers;
563 char servers_line[1024];
568 /* remove screen number from the display name */
569 memset(disp_name, 0, sizeof(disp_name));
570 strncpy(disp_name, host_name ? host_name : tty_line, sizeof(disp_name)-1);
571 pos = strrchr(disp_name, ':');
573 pos = strchr(pos, '.');
577 sysnerr ((int)(long)(ttys = fopen (ttys_file, "r")), ttys_file);
578 while ((c = getc (ttys)) != EOF)
586 (void) fclose (ttys);
587 sysnerr ((int)(long)(servers = fopen (servers_file, "r")), servers_file);
589 len = strlen (disp_name);
591 while (fgets (servers_line, sizeof (servers_line), servers)) {
592 if (column0 && *servers_line != '#') {
593 if (!strncmp (disp_name, servers_line, len) &&
594 (servers_line[len] == ' ' ||
595 servers_line[len] == '\t'))
599 if (servers_line[strlen(servers_line)-1] != '\n')
605 * display not found in Xservers file - allocate utmp entry dinamically
607 return findslot (tty_line, host_name, addp, slot);
611 * find a free utmp slot for the X display. This allocates a new entry
612 * past the regular tty entries if necessary, reusing existing entries
613 * (identified by (line,hostname)) if possible.
616 findslot (char *line_name, char *host_name, int addp, int slot)
623 syserr(utmp = open (utmp_file, O_RDONLY), "open utmp");
626 * first, try to locate a previous entry for this display
627 * also record location of a free slots in case we need a new one
629 syserr ((int) lseek (utmp, (long) slot * sizeof (struct utmp), 0), "lseek");
634 while (read (utmp, (char *) &entry, sizeof (entry)) == sizeof (entry)) {
635 if (strncmp(entry.ut_line, line_name,
636 sizeof(entry.ut_line)) == 0
637 #ifdef HAVE_STRUCT_UTMP_UT_HOST
639 strncmp(entry.ut_host, host_name,
640 sizeof(entry.ut_host)) == 0
646 if (freeslot < 0 && *entry.ut_name == '\0')
656 return 0; /* trying to delete a non-existing entry */
657 else if (freeslot < 0)
658 return slot; /* first slot past current entries */