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;
95 static const char *wtmpx_file = NULL;
98 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);
134 "%s: usage %s {-a -d} [-w wtmp-file] [-u utmp-file]"
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);
148 getstring (char ***avp, int *flagp)
161 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
163 syserr (int x, const char *s)
174 sysnerr (int x, const char *s)
184 main (int argc, char **argv)
186 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
194 struct utmp utmp_entry;
197 struct utmpx utmpx_entry;
201 program_name = argv[0];
202 while (*++argv && **argv == '-') {
205 wtmp_file = getstring (&argv, &wflag);
206 if (!strcmp (wtmp_file, "none"))
210 utmp_file = getstring (&argv, &uflag);
211 if (!strcmp (utmp_file, "none"))
216 llog_file = getstring (&argv, &Lflag);
217 if (!strcmp (llog_file, "none"))
222 ttys_file = getstring (&argv, &tflag);
225 line = getstring (&argv, &lflag);
228 host_name = getstring (&argv, &hflag);
231 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
232 slot_number = atoi (getstring (&argv, &sflag));
236 xservers_file = getstring (&argv, &xflag);
245 printf("%s\n", PACKAGE_STRING);
251 usage (!(user_name = *argv++));
252 usage (*argv != NULL);
254 * complain if neither aflag nor dflag are set,
255 * or if both are set.
257 usage (!(aflag ^ dflag));
258 usage (xflag && !lflag);
259 /* set up default file names */
261 wtmp_file = WTMP_FILE;
262 #if defined(USE_UTMPX) && defined(HAVE_UPDWTMPX)
263 wtmpx_file = WTMPX_FILE;
267 utmp_file = UTMP_FILE;
268 #if defined(USE_UTMPX) && defined(HAVE_UTMPXNAME)
269 utmpx_file = UTMPX_FILE;
274 llog_file = LLOG_FILE;
276 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
278 ttys_file = TTYS_FILE;
279 if (!sflag && !utmp_none) {
281 sysnerr (slot_number = Xslot (ttys_file, xservers_file, line, host_name, aflag), "Xslot");
283 sysnerr (slot_number = ttyslot (), "ttyslot");
287 sysnerr ((line = ttyname (0)) != NULL, "ttyname");
288 if (strncmp(line, "/dev/", 5) == 0)
291 time (¤t_time);
293 set_utmp (&utmp_entry, line, user_name, host_name, current_time, aflag);
297 /* need to set utmpxname() before calling set_utmpx() for
298 UtmpxIdOpen to work */
299 # ifdef HAVE_UTMPXNAME
300 if (utmpx_file != NULL) {
301 utmpxname (utmpx_file);
304 set_utmpx (&utmpx_entry, line, user_name,
305 host_name, current_time, aflag);
310 # ifdef HAVE_UTMPXNAME
311 if (utmpx_file != NULL)
315 (void) getutxid (&utmpx_entry);
316 pututxline (&utmpx_entry);
321 # ifdef HAVE_PUTUTLINE
322 utmpname (utmp_file);
324 (void) getutid (&utmp_entry);
325 pututline (&utmp_entry);
328 utmp = open (utmp_file, O_RDWR);
330 syserr ((int) lseek (utmp, (long) slot_number * sizeof (struct utmp), 0), "lseek");
331 sysnerr (write (utmp, (char *) &utmp_entry, sizeof (utmp_entry))
332 == sizeof (utmp_entry), "write utmp entry");
336 #endif /* USE_UTMP */
340 # ifdef HAVE_UPDWTMPX
341 if (wtmpx_file != NULL) {
342 updwtmpx(wtmpx_file, &utmpx_entry);
346 wtmp = open (wtmp_file, O_WRONLY|O_APPEND);
348 sysnerr (write (wtmp, (char *) &utmp_entry, sizeof (utmp_entry))
349 == sizeof (utmp_entry), "write wtmp entry");
355 if (aflag && !llog_none) {
357 struct passwd *pwd = getpwnam(user_name);
359 sysnerr( pwd != NULL, "get user id");
360 llog = open (llog_file, O_RDWR);
365 sysnerr (lseek(llog, (long) (pwd->pw_uid*sizeof(ll)), 0)
366 != -1, "seeking lastlog entry");
367 memset(&ll, 0, sizeof(ll));
368 ll.ll_time = current_time;
370 (void) strncpy (ll.ll_line, line, sizeof (ll.ll_line));
372 (void) strncpy (ll.ll_host, host_name, sizeof (ll.ll_host));
374 sysnerr (write (llog, (char *) &ll, sizeof (ll))
375 == sizeof (ll), "write lastlog entry");
384 * fill in the appropriate records of the utmp entry
389 set_utmp (struct utmp *u, char *line, char *user, char *host, time_t date, int addp)
391 memset (u, 0, sizeof (*u));
393 (void) strncpy (u->ut_line, line, sizeof (u->ut_line));
395 memset (u->ut_line, 0, sizeof (u->ut_line));
397 (void) strncpy (u->ut_name, user, sizeof (u->ut_name));
399 memset (u->ut_name, 0, sizeof (u->ut_name));
400 #ifdef HAVE_STRUCT_UTMP_UT_ID
404 * this is a bit crufty, but
405 * follows the apparent conventions in
406 * the ttys file. ut_id is only 4 bytes
407 * long, and the last 4 bytes of the line
408 * name are written into it, left justified.
411 if (i >= sizeof (u->ut_id))
412 i -= sizeof (u->ut_id);
415 (void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
417 memset (u->ut_id, 0, sizeof (u->ut_id));
419 #ifdef HAVE_STRUCT_UTMP_UT_PID
421 u->ut_pid = getppid ();
425 #ifdef HAVE_STRUCT_UTMP_UT_TYPE
427 u->ut_type = USER_PROCESS;
429 u->ut_type = DEAD_PROCESS;
431 #ifdef HAVE_STRUCT_UTMP_UT_HOST
433 (void) strncpy (u->ut_host, host, sizeof (u->ut_host));
435 memset (u->ut_host, 0, sizeof (u->ut_host));
439 #endif /* USE_UTMP */
443 UtmpxIdOpen( char *utmpId )
445 struct utmpx *u; /* pointer to entry in utmp file */
446 int status = 1; /* return code */
450 while ( (u = getutxent()) != NULL ) {
452 if ( (strncmp(u->ut_id, utmpId, 4) == 0 ) &&
453 u->ut_type != DEAD_PROCESS ) {
465 set_utmpx (struct utmpx *u, const char *line, const char *user,
466 const char *host, time_t date, int addp)
468 static const char letters[] =
469 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
473 if(strcmp(line, ":0") == 0)
474 (void) strcpy(u->ut_line, "console");
476 (void) strncpy (u->ut_line, line, sizeof (u->ut_line));
478 strncpy(u->ut_host, line, sizeof(u->ut_host));
479 #ifdef HAVE_STRUCT_UTMPX_UT_SYSLEN
480 u->ut_syslen = strlen(line);
484 memset (u->ut_line, 0, sizeof (u->ut_line));
486 (void) strncpy (u->ut_user, user, sizeof (u->ut_user));
488 memset (u->ut_user, 0, sizeof (u->ut_user));
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.
500 if (i >= sizeof (u->ut_id))
501 i -= sizeof (u->ut_id);
504 (void) strncpy (u->ut_id, line + i, sizeof (u->ut_id));
506 /* make sure there is no entry using identical ut_id */
507 if (!UtmpxIdOpen(u->ut_id) && addp) {
508 int limit = sizeof(letters) - 1;
511 u->ut_id[1] = line[i];
512 u->ut_id[2] = line[i+1];
513 u->ut_id[3] = line[i+2];
515 u->ut_id[0] = letters[t];
517 } while (!UtmpxIdOpen(u->ut_id) && (t < limit));
519 if (!addp && strstr(line, ":") != NULL) {
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,
533 memset (u->ut_id, 0, sizeof (u->ut_id));
536 u->ut_pid = getppid ();
537 u->ut_type = USER_PROCESS;
540 u->ut_type = DEAD_PROCESS;
542 u->ut_tv.tv_sec = date;
543 u->ut_tv.tv_usec = 0;
545 #endif /* USE_UTMPX */
547 #if defined(USE_UTMP) && !defined(HAVE_PUTUTLINE)
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).
558 Xslot (char *ttys_file, char *servers_file, char *tty_line, char *host_name,
561 FILE *ttys, *servers;
565 char servers_line[1024];
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, ':');
575 pos = strchr(pos, '.');
579 sysnerr ((int)(long)(ttys = fopen (ttys_file, "r")), ttys_file);
580 while ((c = getc (ttys)) != EOF)
588 (void) fclose (ttys);
589 sysnerr ((int)(long)(servers = fopen (servers_file, "r")), servers_file);
591 len = strlen (disp_name);
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'))
601 if (servers_line[strlen(servers_line)-1] != '\n')
607 * display not found in Xservers file - allocate utmp entry dinamically
609 return findslot (tty_line, host_name, addp, slot);
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.
618 findslot (char *line_name, char *host_name, int addp, int slot)
625 syserr(utmp = open (utmp_file, O_RDONLY), "open utmp");
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
631 syserr ((int) lseek (utmp, (long) slot * sizeof (struct utmp), 0), "lseek");
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 #ifdef HAVE_STRUCT_UTMP_UT_HOST
641 strncmp(entry.ut_host, host_name,
642 sizeof(entry.ut_host)) == 0
648 if (freeslot < 0 && *entry.ut_name == '\0')
658 return 0; /* trying to delete a non-existing entry */
659 else if (freeslot < 0)
660 return slot; /* first slot past current entries */