1 /*
2  * pty_update_utmp: Update or create a utmp entry
3  *
4  * Copyright 1995, 2001 by the Massachusetts Institute of Technology.
5  *
6  * Permission to use, copy, modify, and distribute this software and
7  * its documentation for any purpose and without fee is hereby
8  * granted, provided that the above copyright notice appear in all
9  * copies and that both that copyright notice and this permission
10  * notice appear in supporting documentation, and that the name of
11  * M.I.T. not be used in advertising or publicity pertaining to
12  * distribution of the software without specific, written prior
13  * permission.  Furthermore if you modify this software you must label
14  * your software as modified software and not distribute it in such a
15  * fashion that it might be confused with the original M.I.T. software.
16  * M.I.T. makes no representations about the suitability
17  * of this software for any purpose.  It is provided "as is" without
18  * express or implied warranty.
19  */
20 
21 /*
22  * Rant about the historical vagaries of utmp:
23  * -------------------------------------------
24  *
25  * There exist many subtly incompatible incarnations of utmp, ranging
26  * from BSD to System V to Unix98 and everywhere in between.  This
27  * rant attempts to collect in one place as much knowledge as possible
28  * about this portability nightmare.
29  *
30  * BSD:
31  * ----
32  *
33  * The simplest (and earliest? possibly dating back to Version 7...)
34  * case is 4.x BSD utmp/wtmp.  There are no auxiliary files.  There is
35  * only a struct utmp, declared in utmp.h.  Its contents usually
36  * include:
37  *
38  *	char ut_line[]
39  *	char ut_name[]
40  *	char ut_host[]
41  *	long ut_time
42  *
43  * The meanings of these fields follow their names reasonbly well.
44  * The ut_line field usually is the pathname of the tty device
45  * associated with the login, with the leading "/dev/" stripped off.
46  *
47  * It is believed that ut_host is nul-terminated, while the other
48  * strings are merely nul-padded.
49  *
50  * Generally, ut_name is an empty string for a logout record in both
51  * utmp and wtmp.  For entries made by the window system or other
52  * terminal emulation stuff, ut_host is an empty string (at least
53  * under SunOS 4.x, it seems).  The macro nonuser() is used to
54  * determine this if a utmp entry is made by the window system on at
55  * least SunOS 4.x.
56  *
57  * The native login never clears its own utmp entry or writes its own
58  * logout record; its parent (one of init, rlogind, telnetd, etc.)
59  * should handle that.  In theory, getty could do that, but getty
60  * usually doesn't fork to exec login.
61  *
62  * Old (c. 1984) System V:
63  * -----------------------
64  *
65  * This is partially conjecture, based on some reading of
66  * /usr/xpg2include/utmp.h on a SunOS 4.x system.  There appears to
67  * only be a struct utmp, declared in utmp.h.  It is likely used for
68  * both utmp and wtmp files.  It is quite likely that the utmp is only
69  * supposed to be accessed via the getutline()/pututline() API.  The
70  * contents of struct utmp seem to include:
71  *
72  *	char	ut_user[]
73  *	char	ut_id[]
74  *	char	ut_line[]
75  *	short	ut_pid
76  *	short	ut_type
77  *	struct exit_status ut_exit
78  *	time_t	ut_time
79  *
80  * On these systems, ut_name is often #define'ed to be ut_user to be
81  * somewhat compatible with the BSD-style utmp.  Note that there is
82  * not necessarily a ut_host field in this utmp structure.
83  *
84  * The ut_id field bears some explanation.  The systems that use this
85  * style of utmp also use a sysV-ish init, which starts processes out
86  * of /etc/inittab rather than /etc/ttys, and has the concept of
87  * runlevels.  The first field in each line of /etc/inittab contains a
88  * unique ID field.  init probably gets really confused if there are
89  * conflicts here.  Every process that init starts gets its own entry
90  * written to utmp.
91  *
92  * It is possible for multiple entries to have the same ut_line but
93  * different ut_id values, since the sysadmin will be responsible for
94  * assigning values to ut_id.  Usually, ut_id is four characters,
95  * while the permissible unique ID values for entries in /etc/inittab
96  * are constrained to two characters, but this is not always the
97  * case.  In the case where we are emulating the vendor's login
98  * program and being run out of getty, we need to account for which
99  * value of ut_id was used by the getty, since pututline() will search
100  * based on ut_id and not ut_line for some reason.
101  *
102  * The ut_pid and ut_type fields are used for bookkeeping by init.
103  * The ut_type field gets the value INIT_PROCESS for processes started
104  * by init.  It gets the value LOGIN_PROCESS if it is a process that
105  * is prompting for a login name, and it gets the value USER_PROCESS
106  * for an actual valid login.  When the process dies, either init
107  * cleans up after it and records a DEAD_PROCESS entry in utmp, or the
108  * process itself does so.  It's not completely clear which actually
109  * happens, though it is quite possible that init only cleans up after
110  * processes that it starts itself.
111  *
112  * Other values of ut_type exist; they're largely internal bookkeeping
113  * for init's runlevels and such, and don't really interest this
114  * library at all.
115  *
116  * The ut_exit field contains the following members:
117  *
118  *	short	e_termination
119  *	short	e_exit
120  *
121  * It is not clear how these values are used; presumably they record
122  * the process termination status of dead processes.
123  *
124  * There is no uniform API for manipulating wtmp on systems that use
125  * this sort of utmp structure; it can be assumed that the structure
126  * can be directly written to the wtmp file.
127  *
128  * Unix98:
129  * -------
130  *
131  * This description also likely applies to later System V derivatives
132  * as well as systems conforming to earlier X/Open standards such as
133  * XPG4.  There is a new header, utmpx.h, which defines a struct utmpx
134  * and a new getutxline()/pututxline() API for accessing it.  Some
135  * systems actually have a utmpx file on disk; others use the utmpx
136  * API to access a file named utmp, just to further confuse matters.
137  *
138  * The utmpx structure is guaranteed (by Unix98) to contain at least
139  * the following:
140  *
141  *	char	ut_user[]
142  *	char	ut_line[]
143  *	char	ut_id[]
144  *	pid_t	ut_pid
145  *	short	ut_type
146  *	struct timeval ut_tv
147  *
148  * It is not guaranteed to contain, but often does contain, the
149  * following:
150  *
151  *	char	ut_host[]
152  *	int	ut_syslen
153  *	int	ut_session
154  *	struct exit_status ut_exit
155  *
156  * The ut_syslen field, on systems that contain it, contains the
157  * number of significant characters in ut_host, including the
158  * terminating nul character.
159  *
160  * The main difference between this struct utmpx and the struct utmp
161  * used by early sysV derivatives is the change from a time_t or long
162  * for ut_time to a struct timeval for ut_tv.
163  *
164  * Comments in various header files imply that ut_session is used for
165  * window systems, but it's not clear how.  Perhaps it contains the
166  * session ID of the session running the window system, e.g. the xdm
167  * or X server on an X11 system.
168  *
169  * Most of the description of the earlier sysV format probably applies
170  * here, with suitable changes of names.  On systems that maintain
171  * utmpx and utmp files in parallel, it is assumed that using the
172  * pututxline() API is sufficient to keep them in sync.  There are no
173  * known counterexamples to this.
174  *
175  * Nevertheless, there are, on some systems, API functions getutmp()
176  * and getutmpx() that appear to convert from struct utmpx to struct
177  * utmp and vice versa.  This could be useful when there is a wtmp
178  * file but not a corresponding wtmpx file.
179  *
180  * Incidentally, ut_exit is sometimes present in the struct utmp but
181  * not the struct utmpx for a given system.  Sometimes, it exists in
182  * both, but contains differently named members.  It's probably one of
183  * the least portable pieces in this whole mess.
184  *
185  * Known Quirks of Specific OSes:
186  * ------------------------------
187  *
188  * Solaris 2.x:
189  *
190  * Has utmpd, which will automatically clean up utmpx, utmp, wtmpx,
191  * wtmp after process termination, provided that pututxline() was
192  * used.
193  *
194  * Solaris 8 seems to have a bug in utmpname() that causes
195  * garbage filenames to be generated.  Solaris 7 (and possibly Solaris
196  * 8) have a bug in utmpxname() that prevents them from looking at
197  * anything other than /var/adm/utmpx, it seems.  For some reason,
198  * though, utmpname() goes and looks at the corresponding utmpx file.
199  *
200  * Solaris 7 (and may be 8 as well) has a bug in pututline() that
201  * interacts badly with prior invocation of getutline(): if
202  * getutline() finds an entry, calling pututline() without first
203  * calling setutent() will overwrite the record following the one that
204  * was intended.
205  *
206  * Also, ut_exit in utmpx contains ut_e_termination and
207  * ut_e_exit (otherwise it contains the expected e_termination and
208  * e_exit) only if _XPG4_2 is defined and __EXTENSIONS__ is not, which
209  * is not a compilation environment we're likely to encourage.  The
210  * ut_exit field of utmp contains the expected fields.
211  *
212  * If _XPG4_2 is not defined or __EXTENSIONS__ is defined, the
213  * functions getutmp(), getutmpx(), updwtmp(), and updwtmpx() are
214  * available, as well as the undocumented functions makeutx() and
215  * modutx().
216  *
217  * All the files utmp, utmpx, wtmp, and wtmpx exist.
218  *
219  * HP-UX 10.x:
220  *
221  * There is a curious interaction between how we allocate pty masters
222  * and how ttyname() works.  It seems that if /dev/ptmx/clone is
223  * opened, a call to ptsname() on the master fd gets a filename of the
224  * form /dev/pty/tty[pqrs][0-9a-f], while ttyname() called on a fd
225  * opened with that filename returns a filename of the form
226  * /dev/tty[pqrs][0-9a-f] instead.  These two filenames are actually
227  * hardlinks to the same special device node, so it shouldn't be a
228  * security problem.
229  *
230  * We can't call ttyname() in the parent because it would involve
231  * possibly acquiring a controlling terminal (which would be
232  * potentially problematic), so we have to resort to some trickery in
233  * order to ensure that the ut_line in the wtmp logout and login
234  * records match.  If they don't match, various utilities such as last
235  * will get confused.  Of course it's likely an OS bug that ttyname()
236  * and ptsname() are inconsistent in this way, but it's one that isn't
237  * too painful to work around.
238  *
239  * It seems that the HP-UX native telnetd has problems similar to ours
240  * in this area, though it manages to write the correct logout record
241  * to wtmp somehow.  It probably does basically what we do here:
242  * search for a record with a matching ut_pid and grab its ut_line for
243  * writing into the logout record.  Interestingly enough, its
244  * LOGIN_PROCESS record is of the form pty/tty[pqrs][0-9][a-f].
245  *
246  * Uses four-character unique IDs for /etc/inittab, which means that
247  * programs not running out of init should use two-character ut_id
248  * fields to avoid conflict.
249  *
250  * In utmpx, ut_exit contains __e_termination and __e_exit, while
251  * ut_exit in utmp contains the expected fields.
252  *
253  * There is no wtmpx file, despite there being utmp and utmpx files.
254  *
255  * HP-UX 11.23:
256  *
257  * In addition to other HP-UX issues, 11.23 includes yet another utmp
258  * management interface in utmps.h.  This interface updates a umtpd
259  * daemon which then manages local files.  Directly accessing the files
260  * through the existing, yet deprecated, utmp.h interface results in
261  * nothing.
262  *
263  * Irix 6.x:
264  *
265  * In utmpx, ut_exit contains __e_termination and __e_exit, which get
266  * #define aliases e_termination and e_exit if _NO_XOPEN4 is true.
267  * Curiously enough, utmp.h declares ut_exit to have __e_termination
268  * and __e_exit as well, but does #define e_termination
269  * __e_termination, etc. if another header (utmpx.h) hasn't already
270  * declared struct __exit_status.  It seems that the default
271  * compilation environment has the effect of making _NO_XOPEN4 true
272  * though.
273  *
274  * If _NO_XOPEN4 is true, getutmp(), getutmpx(), updwtmp(), and
275  * updwtmpx() are available, as well as the undocumented functions
276  * makeutx() and modutx().
277  *
278  * All the files utmp, utmpx, wtmp, and wtmpx exist.
279  *
280  * Tru64 Unix 4.x:
281  *
282  * In utmpx, ut_exit contains ut_termination and ut_exit, while utmp
283  * contains the expected fields.  The files utmp and wtmp seem to
284  * exist, but not utmpx or wtmpx.
285  *
286  * When writing a logout entry, the presence of a non-empty username
287  * confuses last.
288  *
289  * AIX 4.3.x:
290  *
291  * The ut_exit field seems to exist in utmp, but not utmpx. The files
292  * utmp and wtmp seem to exist, but not utmpx, or wtmpx.
293  *
294  * libpty Implementation Decisions:
295  * --------------------------------
296  *
297  * We choose to use the pututxline() whenever possible, falling back
298  * to pututline() and calling write() to write out struct utmp if
299  * necessary.  The code to handle pututxline() and pututline() is
300  * rather similar, since the structure members are quite similar, and
301  * we make the assumption that it will never be necessary to call
302  * both.  This allows us to avoid duplicating lots of code, by means
303  * of some slightly demented macros.
304  *
305  * If neither pututxline() nor pututline() are available, we assume
306  * BSD-style utmp files and behave accordingly, writing the structure
307  * out to disk ourselves.
308  *
309  * On systems where updwtmpx() or updwtmp() are available, we use
310  * those to update the wtmpx or wtmp file.  When they're not
311  * available, we write the utmpx or utmp structure out to disk
312  * ourselves, though sometimes conversion from utmpx to utmp format is
313  * needed.
314  *
315  * We assume that at logout the system is ok with with having an empty
316  * username both in utmp and wtmp.
317  */
318 
319 #include "pty-int.h"
320 #include "k5-platform.h"
321 
322 #if !defined(UTMP_FILE) && defined(_PATH_UTMP)
323 #define UTMP_FILE _PATH_UTMP
324 #endif
325 
326 /* if it is *still* missing, assume SunOS */
327 #ifndef UTMP_FILE
328 #define	UTMP_FILE	"/etc/utmp"
329 #endif
330 
331 /*
332  * The following grossness exists to avoid duplicating lots of code
333  * between the cases where we have an old-style sysV utmp and where we
334  * have a modern (Unix98 or XPG4) utmpx, or the new (hp-ux 11.23) utmps.
335  * See the above history rant for further explanation.
336  */
337 #if defined(HAVE_SETUTXENT) || defined(HAVE_SETUTENT) || defined(HAVE_SETUTSENT)
338 #ifdef HAVE_SETUTSENT
339 #include <utmps.h>
340 #define PTY_STRUCT_UTMPX struct utmps
341 #define PTY_SETUTXENT setutsent
342 #define PTY_GETUTXENT GETUTSENT
343 #define PTY_GETUTXLINE GETUTSLINE
344 #define PTY_PUTUTXLINE PUTUTSLINE
345 #define PTY_ENDUTXENT endutsent
346 #else
347 #ifdef HAVE_SETUTXENT
348 #define PTY_STRUCT_UTMPX struct utmpx
349 #define PTY_SETUTXENT setutxent
350 #define PTY_GETUTXENT getutxent
351 #define PTY_GETUTXLINE getutxline
352 #define PTY_PUTUTXLINE pututxline
353 #define PTY_ENDUTXENT endutxent
354 #else
355 #define PTY_STRUCT_UTMPX struct utmp
356 #define PTY_SETUTXENT setutent
357 #define PTY_GETUTXENT getutent
358 #define PTY_GETUTXLINE getutline
359 #define PTY_PUTUTXLINE pututline
360 #define PTY_ENDUTXENT endutent
361 #endif
362 #endif
363 static int better(const PTY_STRUCT_UTMPX *, const PTY_STRUCT_UTMPX *,
364 		  const PTY_STRUCT_UTMPX *);
365 static int match_pid(const PTY_STRUCT_UTMPX *,
366 		     const PTY_STRUCT_UTMPX *);
367 static PTY_STRUCT_UTMPX *best_utxent(const PTY_STRUCT_UTMPX *);
368 
369 /*
370  * Utility function to determine whether A is a better match for
371  * SEARCH than B.  Should only be called by best_utxent().
372  */
373 static int
better(const PTY_STRUCT_UTMPX * search,const PTY_STRUCT_UTMPX * a,const PTY_STRUCT_UTMPX * b)374 better(const PTY_STRUCT_UTMPX *search,
375        const PTY_STRUCT_UTMPX *a, const PTY_STRUCT_UTMPX *b)
376 {
377     if (strncmp(search->ut_id, b->ut_id, sizeof(b->ut_id))) {
378 	if (!strncmp(search->ut_id, a->ut_id, sizeof(a->ut_id))) {
379 	    return 1;
380 	}
381     }
382 
383     if (strncmp(a->ut_id, b->ut_id, sizeof(b->ut_id))) {
384 	/* Got different UT_IDs; find the right one. */
385 	if (!strncmp(search->ut_id, b->ut_id, sizeof(b->ut_id))) {
386 	    /* Old entry already matches; use it. */
387 	    return 0;
388 	}
389 	if (a->ut_type == LOGIN_PROCESS
390 	    && b->ut_type != LOGIN_PROCESS) {
391 	    /* Prefer LOGIN_PROCESS */
392 	    return 1;
393 	}
394 	if (search->ut_type == DEAD_PROCESS
395 	    && a->ut_type == USER_PROCESS
396 	    && b->ut_type != USER_PROCESS) {
397 	    /*
398 	     * Try USER_PROCESS if we're entering a DEAD_PROCESS.
399 	     */
400 	    return 1;
401 	}
402 	return 0;
403     } else {
404 	/*
405 	 * Bad juju.  We shouldn't get two entries with identical
406 	 * ut_id fields for the same value of ut_line.  pututxline()
407 	 * will probably pick the first entry, in spite of the strange
408 	 * state of utmpx, if we rewind with setutxent() first.
409 	 *
410 	 * For now, return 0, to force the earlier entry to be used.
411 	 */
412 	return 0;
413     }
414 }
415 
416 static int
match_pid(const PTY_STRUCT_UTMPX * search,const PTY_STRUCT_UTMPX * u)417 match_pid(const PTY_STRUCT_UTMPX *search, const PTY_STRUCT_UTMPX *u)
418 {
419     if (u->ut_type != LOGIN_PROCESS && u->ut_type != USER_PROCESS)
420 	return 0;
421     if (u->ut_pid == search->ut_pid) {
422 	/*
423 	 * One of ut_line or ut_id should match, else some nastiness
424 	 * may result.  We can fall back to searching by ut_line if
425 	 * need be.  This should only really break if we're login.krb5
426 	 * running out of getty, or we're cleaning up after the vendor
427 	 * login, and either the vendor login or the getty has
428 	 * different ideas than we do of what both ut_id and ut_line
429 	 * should be.  It should be rare, though.  We may want to
430 	 * remove this restriction later.
431 	 */
432 	if (!strncmp(u->ut_line, search->ut_line, sizeof(u->ut_line)))
433 	    return 1;
434 	if (!strncmp(u->ut_id, search->ut_id, sizeof(u->ut_id)))
435 	    return 1;
436     }
437     return 0;
438 }
439 
440 /*
441  * This expects to be called with SEARCH pointing to a struct utmpx
442  * with its ut_type equal to USER_PROCESS or DEAD_PROCESS, since if
443  * we're making a LOGIN_PROCESS entry, we presumably don't care about
444  * preserving existing state.  At the very least, the ut_pid, ut_line,
445  * ut_id, and ut_type fields must be filled in by the caller.
446  */
447 static PTY_STRUCT_UTMPX *
best_utxent(const PTY_STRUCT_UTMPX * search)448 best_utxent(const PTY_STRUCT_UTMPX *search)
449 {
450     PTY_STRUCT_UTMPX utxtmp, *utxp;
451     int i, best;
452 
453     memset(&utxtmp, 0, sizeof(utxtmp));
454 
455     /*
456      * First, search based on pid, but only if non-zero.
457      */
458     if (search->ut_pid) {
459 	i = 0;
460 	PTY_SETUTXENT();
461 	while ((utxp = PTY_GETUTXENT()) != NULL) {
462 	    if (match_pid(search, utxp)) {
463 		return utxp;
464 	    }
465 	    i++;
466 	}
467     }
468     /*
469      * Uh-oh, someone didn't enter our pid.  Try valiantly to search
470      * by terminal line.
471      */
472     i = 0;
473     best = -1;
474     PTY_SETUTXENT();
475     while ((utxp = PTY_GETUTXLINE(search)) != NULL) {
476 	if (better(search, utxp, &utxtmp)) {
477 	    utxtmp = *utxp;
478 	    best = i;
479 	}
480 	memset(utxp, 0, sizeof(*utxp));
481 	i++;
482     }
483     if (best == -1)
484 	return NULL;
485     PTY_SETUTXENT();
486     for (i = 0; i <= best; i++) {
487 	if (utxp != NULL)
488 	    memset(utxp, 0, sizeof(*utxp));
489 	utxp = PTY_GETUTXLINE(search);
490     }
491     return utxp;
492 }
493 
494 /*
495  * All calls to this function for a given login session must have the
496  * pids be equal; various things will break if this is not the case,
497  * since we do some searching based on the pid.  Note that if a parent
498  * process calls this via pty_cleanup(), it should still pass the
499  * child's pid rather than its own.
500  */
501 long
pty_update_utmp(int process_type,int pid,const char * username,const char * line,const char * host,int flags)502 pty_update_utmp(int process_type, int pid, const char *username,
503 		    const char *line, const char *host, int flags)
504 {
505     PTY_STRUCT_UTMPX utx, *utxtmp, utx2;
506     const char *cp;
507     size_t len;
508     char utmp_id[5];
509     struct timeval tv;
510 
511     /*
512      * Zero things out in case there are fields we don't handle here.
513      * They tend to be non-portable anyway.
514      */
515     memset(&utx, 0, sizeof(utx));
516     utxtmp = NULL;
517     cp = line;
518     if (strncmp(cp, "/dev/", sizeof("/dev/") - 1) == 0)
519 	cp += sizeof("/dev/") - 1;
520     strncpy(utx.ut_line, cp, sizeof(utx.ut_line));
521     utx.ut_pid = pid;
522     switch (process_type) {
523     case PTY_LOGIN_PROCESS:
524 	utx.ut_type = LOGIN_PROCESS;
525 	break;
526     case PTY_USER_PROCESS:
527 	utx.ut_type = USER_PROCESS;
528 	break;
529     case PTY_DEAD_PROCESS:
530 	utx.ut_type = DEAD_PROCESS;
531 	break;
532     default:
533 	return PTY_UPDATE_UTMP_PROCTYPE_INVALID;
534     }
535     len = strlen(line);
536     if (len >= 2) {
537 	cp = line + len - 1;
538 	if (*(cp - 1) != '/')
539 	    cp--;		/* last two characters, unless it's a / */
540     } else
541 	cp = line;
542     /*
543      * HP-UX has mostly 4-character inittab ids, while most other sysV
544      * variants use only 2-charcter inittab ids, so to avoid
545      * conflicts, we pick 2-character ut_ids for our own use.  We may
546      * want to feature-test for this, but it would be somewhat of a
547      * pain, and would eit cross-compiling.
548      */
549 #ifdef __hpux
550     strlcpy(utmp_id, cp, sizeof(utmp_id));
551 #else
552     if (len > 2 && *(cp - 1) != '/')
553 	snprintf(utmp_id, sizeof(utmp_id), "k%s", cp - 1);
554     else
555 	snprintf(utmp_id, sizeof(utmp_id), "k0%s", cp);
556 #endif
557     strncpy(utx.ut_id, utmp_id, sizeof(utx.ut_id));
558     /*
559      * Get existing utmpx entry for PID or LINE, if any, so we can
560      * copy some stuff from it.  This is particularly important if we
561      * are login.krb5 and are running out of getty, since getty will
562      * have written the entry for the line with ut_type ==
563      * LOGIN_PROCESS, and what it has recorded in ut_id may not be
564      * what we come up with, since that's up to the whim of the
565      * sysadmin who writes the inittab entry.
566      *
567      * Note that we may be screwed if we try to write a logout record
568      * for a vendor's login program, since it may construct ut_line
569      * and ut_id differently from us; even though we search on ut_pid,
570      * we validate against ut_id or ut_line to sanity-check.  We may
571      * want to rethink whether to actually include this check, since
572      * it should be highly unlikely that there will be a bogus entry
573      * in utmpx matching our pid.
574      */
575     if (process_type != PTY_LOGIN_PROCESS)
576 	utxtmp = best_utxent(&utx);
577 
578     if (gettimeofday(&tv, NULL))
579 	return errno;
580 #ifdef HAVE_SETUTXENT
581     utx.ut_tv.tv_sec = tv.tv_sec;
582     utx.ut_tv.tv_usec = tv.tv_usec;
583 #else
584     utx.ut_time = tv.tv_sec;
585 #endif
586     /*
587      * On what system is there not ut_host?  Unix98 doesn't mandate
588      * this field, but we have yet to see a system that supports utmpx
589      * that doesn't have it.  For what it's worth, some ancient utmp
590      * headers on svr4 systems imply that there's no ut_host in struct
591      * utmp...
592      */
593 #if (defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMPX_UT_HOST))	\
594 	|| (!defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMP_UT_HOST))
595     if (host != NULL) {
596 	strncpy(utx.ut_host, host, sizeof(utx.ut_host));
597 	/* Unlike other things in utmpx, ut_host is nul-terminated? */
598 	utx.ut_host[sizeof(utx.ut_host) - 1] = '\0';
599     } else
600 	utx.ut_host[0] = '\0';
601 #if (defined(HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMPX_UT_SYSLEN))	\
602 	|| (!defined (HAVE_SETUTXENT) && defined(HAVE_STRUCT_UTMP_UT_SYSLEN))
603     if (host != NULL)
604 	utx.ut_syslen = strlen(utx.ut_host) + 1;
605     else
606 	utx.ut_syslen = 0;
607 #endif
608 #endif
609 
610     /* XXX deal with ut_addr? */
611 
612     if (utxtmp != NULL) {
613 	/*
614 	 * For entries not of type LOGIN_PROCESS, override some stuff
615 	 * with what was in the previous entry we found, if any.
616 	 */
617 	strncpy(utx.ut_id, utxtmp->ut_id, sizeof(utx.ut_id));
618 	utx.ut_pid = utxtmp->ut_pid;
619     }
620 
621     strncpy(utx.ut_user, username, sizeof(utx.ut_user));
622 
623     /*
624      * Make a copy now and deal with copying relevant things out of
625      * utxtmp in case setutxline() or pututxline() clobbers utxtmp.
626      * (After all, the returned pointer from the getutx*() functions
627      * is allowed to point to static storage that may get overwritten
628      * by subsequent calls to related functions.)
629      */
630     utx2 = utx;
631     if (process_type == PTY_DEAD_PROCESS && utxtmp != NULL) {
632 	/*
633 	 * Use ut_line from old entry to avoid confusing last on
634 	 * HP-UX.
635 	 */
636 	strncpy(utx2.ut_line, utxtmp->ut_line, sizeof(utx2.ut_line));
637     }
638 
639     PTY_SETUTXENT();
640     PTY_PUTUTXLINE(&utx);
641     PTY_ENDUTXENT();
642 
643     /* Don't record LOGIN_PROCESS entries. */
644     if (process_type == PTY_LOGIN_PROCESS)
645 	return 0;
646 
647 #ifdef HAVE_SETUTXENT
648     return ptyint_update_wtmpx(&utx2);
649 #else
650     return ptyint_update_wtmp(&utx2);
651 #endif
652 }
653 
654 #else /* !(HAVE_SETUTXENT || HAVE_SETUTENT) */
655 
656 long
pty_update_utmp(int process_type,int pid,const char * username,const char * line,const char * host,int flags)657 pty_update_utmp(int process_type, int pid, const char *username,
658 		const char *line, const char *host, int flags)
659 {
660     struct utmp ent, ut;
661     const char *cp;
662     int tty, lc, fd;
663     off_t seekpos;
664     ssize_t ret;
665     struct stat statb;
666 
667     memset(&ent, 0, sizeof(ent));
668 #ifdef HAVE_STRUCT_UTMP_UT_HOST
669     if (host)
670 	strncpy(ent.ut_host, host, sizeof(ent.ut_host));
671 #endif
672     strncpy(ent.ut_name, username, sizeof(ent.ut_name));
673     cp = line;
674     if (strncmp(cp, "/dev/", sizeof("/dev/") - 1) == 0)
675 	cp += sizeof("/dev/") - 1;
676     strncpy(ent.ut_line, cp, sizeof(ent.ut_line));
677     (void)time(&ent.ut_time);
678 
679     if (flags & PTY_TTYSLOT_USABLE)
680 	tty = ttyslot();
681     else {
682 	tty = -1;
683 	fd = open(UTMP_FILE, O_RDONLY);
684 	if (fd == -1)
685 	    return errno;
686 	for (lc = 0; ; lc++) {
687 	    seekpos = lseek(fd, (off_t)(lc * sizeof(struct utmp)), SEEK_SET);
688 	    if (seekpos != (off_t)(lc * sizeof(struct utmp)))
689 		break;
690 	    if (read(fd, (char *) &ut, sizeof(struct utmp))
691 		!= sizeof(struct utmp))
692 		break;
693 	    if (strncmp(ut.ut_line, ent.ut_line, sizeof(ut.ut_line)) == 0) {
694 		tty = lc;
695 		break;
696 	    }
697 	}
698 	close(fd);
699     }
700     if (tty > 0) {
701 	fd = open(UTMP_FILE, O_WRONLY);
702 	if (fd == -1)
703 	    return 0;
704 	if (fstat(fd, &statb)) {
705 	    close(fd);
706 	    return 0;
707 	}
708 	seekpos = lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET);
709 	if (seekpos != (off_t)(tty * sizeof(struct utmp))) {
710 	    close(fd);
711 	    return 0;
712 	}
713 	ret = write(fd, (char *)&ent, sizeof(struct utmp));
714 	if (ret != sizeof(struct utmp)) {
715 	    ftruncate(fd, statb.st_size);
716 	}
717 	close(fd);
718     }
719     /* Don't record LOGIN_PROCESS entries. */
720     if (process_type == PTY_LOGIN_PROCESS)
721 	return 0;
722     else
723 	return ptyint_update_wtmp(&ent);
724 }
725 #endif
726