81612822fac7643bb6657796e935c48b93c1fed2
[oweals/cde.git] / cde / lib / DtTerm / TermPrim / TermPrimSetUtmp.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 #ifndef lint
24 #ifdef  VERBOSE_REV_INFO
25 static char rcs_id[] = "$TOG: TermPrimSetUtmp.c /main/10 1998/04/03 17:11:42 mgreess $";
26 #endif  /* VERBOSE_REV_INFO */
27 #endif  /* lint */
28
29 /*                                                                      *
30  * (c) Copyright 1993, 1994, 1996 Hewlett-Packard Company               *
31  * (c) Copyright 1993, 1994, 1996 International Business Machines Corp. *
32  * (c) Copyright 1993, 1994, 1996 Sun Microsystems, Inc.                *
33  * (c) Copyright 1993, 1994, 1996 Novell, Inc.                          *
34  * (c) Copyright 1996 Digital Equipment Corporation.                    *
35  * (c) Copyright 1996 FUJITSU LIMITED.                                  *
36  * (c) Copyright 1996 Hitachi.                                          *
37  */
38
39 #include <Xm/Xm.h>
40 #include <TermPrimP.h>
41 #include <TermPrimSetUtmp.h>
42 #include <TermPrimUtil.h>
43 #include "TermPrimDebug.h"
44 #include "TermHeader.h"
45
46 /* for sigprocmask... */
47 #include <signal.h>
48
49 /* for open... */
50 #include <fcntl.h>
51
52 /* for getpw*() and getlogin() calls... */
53 #define X_INCLUDE_PWD_H
54 #define X_INCLUDE_NETDB_H
55 #define X_INCLUDE_UNISTD_H
56 #define XOS_USE_XT_LOCKING
57 #include <X11/Xos_r.h>
58
59 #ifdef  linux
60 #define UT_NO_pututline
61 #endif  /* sun */
62
63 #ifdef  __FreeBSD__
64 #if OSMAJORVERSION > 8
65 #define UT_UTMPX
66 #endif
67 #define UT_HOST         ut_host
68 #define UT_NO_pututline
69 #endif
70
71 #ifdef  sun
72 #define UT_UTMPX
73 #define UT_HOST         ut_host
74 #define UT_HOST_LEN     ut_syslen
75 #define UT_NO_pututline
76 #endif  /* sun */
77
78 #ifdef  __hpux
79 #define UT_HOST         ut_host
80 #define UT_ADDR         ut_addr
81 #endif  /* __hpux */
82
83 #ifdef  __AIX
84 #define UT_HOST         ut_host
85 #define UT_NO_pututline
86 #endif  /* __AIX */
87
88
89 /* /etc/utmp include files... */
90 #ifdef  UT_UTMPX
91 #include <utmpx.h>
92 #define getutent                getutxent       
93 #define getutid                 getutxid
94 #define getutline               getutxline
95 #ifdef  NOTDEF
96 #define pututline(entry)        updwtmpx(UTMPX_FILE, entry)
97 #endif  /* NOTDEF */
98 #define pututline               pututxline
99 #define setutent                setutxent
100 #define endutent                endutxent
101
102 #define utmp                    utmpx
103
104 #define ut_time                 ut_tv.tv_sec
105 #else   /* UT_UTMPX */
106 #include <utmp.h>
107 #endif  /* UT_UTMPX */
108
109 /* gethostbyname include files... */
110 #include <sys/socket.h>
111 #include <netinet/in.h>
112
113 typedef struct _utmpInfo {
114     char *utmpLine;
115     struct _utmpInfo *next;
116     struct _utmpInfo *prev;
117 } utmpInfo;
118
119 static utmpInfo _utmpInfoHead;
120 static utmpInfo *utmpInfoHead = &_utmpInfoHead;
121
122 void
123 _DtTermPrimUtmpAddEntry
124 (
125     char                 *utmpLine
126 )
127 {
128     utmpInfo             *utmpInfoTmp;
129     sigset_t              newSigs;
130     sigset_t              oldSigs;
131
132     /* malloc a new entry... */
133     utmpInfoTmp = (utmpInfo *) XtMalloc(sizeof(utmpInfo));
134     (void) memset(utmpInfoTmp, '\0', sizeof(utmpInfo));
135
136     /* fill in the structure... */
137     utmpInfoTmp->utmpLine = (char *) XtMalloc(strlen(utmpLine) + 1);
138     (void) strcpy(utmpInfoTmp->utmpLine, utmpLine);
139
140     /* insert it after the head of the list...
141      */
142     /* block all signals... */
143     (void) sigfillset(&newSigs);
144     (void) sigemptyset(&oldSigs);
145     (void) sigprocmask(SIG_BLOCK, &newSigs, &oldSigs);
146     /* insert the entry into the list... */
147     _DtTermProcessLock();
148     utmpInfoTmp->prev = utmpInfoHead;
149     utmpInfoTmp->next = utmpInfoHead->next;
150     utmpInfoHead->next = utmpInfoTmp;
151     if (utmpInfoTmp->next) {
152         utmpInfoTmp->next->prev = utmpInfoTmp;
153     }
154     _DtTermProcessUnlock();
155     /* restore signals... */
156     (void) sigprocmask(SIG_SETMASK, &oldSigs, (sigset_t *) 0);
157 }
158
159 static void
160 DeleteUtmpInfo
161 (
162     char                 *utmpLine
163 )
164 {
165     utmpInfo             *utmpInfoTmp;
166     sigset_t              newSigs;
167     sigset_t              oldSigs;
168
169     _DtTermProcessLock();
170
171     /* find the entry... */
172     for (utmpInfoTmp = utmpInfoHead->next; utmpInfoTmp;
173             utmpInfoTmp = utmpInfoTmp->next) {
174         if (!strcmp(utmpInfoTmp->utmpLine, utmpLine)) {
175             break;
176         }
177     }
178
179     /* did we find anything... */
180     if (!utmpInfoTmp) {
181         /* not found... */
182         _DtTermProcessUnlock();
183         return;
184     }
185
186     /* delete entry from the list...
187      */
188     /* block all signals... */
189     (void) sigfillset(&newSigs);
190     (void) sigemptyset(&oldSigs);
191     (void) sigprocmask(SIG_BLOCK, &newSigs, &oldSigs);
192     /* remove it... */
193     utmpInfoTmp->prev->next = utmpInfoTmp->next;
194     if (utmpInfoTmp->next) {
195         utmpInfoTmp->next->prev = utmpInfoTmp->prev;
196     }
197     /* restore signals... */
198     (void) sigprocmask(SIG_SETMASK, &oldSigs, (sigset_t *) 0);
199
200     /* free up the data... */
201     if (utmpInfoTmp->utmpLine) {
202         (void) XtFree(utmpInfoTmp->utmpLine);
203         utmpInfoTmp->utmpLine = (char *) 0;
204     }
205     (void) XtFree((char *) utmpInfoTmp);
206     _DtTermProcessUnlock();
207 }
208
209 static char *userName = (char *) 0;
210 Atom _DtTermPrim_XA_UtmpLine = (Atom) 0;
211 #ifdef  UT_ADDR
212 static char *localHostname = (char *) 0;
213 #endif  /* UT_ADDR */
214
215 #ifdef  UT_NO_pututline
216 static struct utmp *
217 _pututline(struct utmp *ut)
218 {
219     (void) pututline(ut);
220     return(ut);
221 }
222 #endif  /* UT_NO_pututline */
223
224 /* the following things should be done in the parent so that they will
225  * be available to all utmp entry creates which are done in the child.
226  * If we wait until we do the create, the info will not be passed back
227  * to the parent, and we will have to do it each and every time...
228  */
229 void
230 _DtTermPrimUtmpInit(Widget w)
231 {
232     char buffer[BUFSIZ];
233     char *c;
234     struct passwd * pw_ret;
235     _Xgetpwparams pw_buf;
236
237     char *namebuf;
238     _Xgetloginparams login_buf;
239
240 #ifdef UT_ADDR
241     struct hostent *            name_ret;
242     _Xgethostbynameparams       name_buf;
243 #endif
244
245     _DtTermProcessLock();
246
247     if (!userName) {
248 /* getpw{uid,nam}_r routines fail on IBM when searching passwd info via NIS.
249    The AIX specific code could be removed after IBM fixes the problem. Note
250    that there could still be a failure on IBM platform if "USER" env variable
251    is not defined. In any case, we would really not want to rely on the env
252    variable.
253 */
254 #if defined(XTHREADS) && defined(XUSE_MTSAFE_API) && defined(AIXV3)
255       if ((c = getenv("USER")) == NULL) {
256 #endif
257         /* get the user's name from:
258          *      -/etc/utmp if possible,
259          *      -/etc/passwd if not.
260          */
261         if ((((namebuf = _XGetlogin(login_buf))) != NULL)
262             && (((pw_ret = _XGetpwnam(namebuf, pw_buf))) != NULL) 
263             && (pw_ret->pw_uid == getuid())) {
264             userName = XtMalloc(strlen(namebuf) + 1);
265             (void) strcpy(userName, namebuf);
266         } else if (((pw_ret = _XGetpwuid(getuid(), pw_buf))) != NULL) {
267             userName = XtMalloc(strlen(pw_ret->pw_name) + 1);
268             (void) strcpy(userName, pw_ret->pw_name);
269         }
270 #if defined(XTHREADS) && defined(XUSE_MTSAFE_API) && defined(AIXV3)
271       }
272       else {
273         userName = XtMalloc(strlen(c) + 1);
274         (void) strcpy(userName, c);
275       }
276 #endif
277     }
278
279     if (!_DtTermPrim_XA_UtmpLine) {
280         _DtTermPrim_XA_UtmpLine = XInternAtom(XtDisplay(w), TermUtmpIdString, False);
281     }
282         
283 #ifdef  UT_ADDR
284     if (!localHostname && !gethostname(buffer, sizeof(buffer)) &&
285         (name_ret = _XGethostbyname(buffer, name_buf)) != NULL) {
286         localHostname = XtMalloc(strlen(buffer) + 1);
287         (void) strcpy(localHostname, buffer);
288     }
289 #endif  /* UT_ADDR */
290     _DtTermProcessUnlock();
291 }
292
293 char *
294 _DtTermPrimUtmpGetUtLine(int pty, char *ptyName)
295 {
296     Boolean closePty = False;
297     char *c;
298
299 #ifdef  DKS
300     /* use the same pty name that everyone else will use (the one
301      * returned by ttyname())...
302      */
303     _Xttynameparams tty_buf;
304
305     /* if we weren't passed a pty, let's try opening ptyName.  By using
306      * O_NOCTTY we are able to open the pty without accidentally becoming
307      * the session leader for it...
308      */
309     if ((pty < 0) && ((pty = open(ptyName, O_RDWR | O_NOCTTY, 0)) >= 0)) {
310         closePty = True;
311     }
312
313     /* if we have a pty, use ttyname to get it's "canonical" name... */
314     if ((pty >= 0) && (c = _XTtyname(pty, tty_buf))) {
315         ptyName = c;
316     }
317
318     /* close the pty if we opened it... */
319     if (closePty) {
320         (void) close(pty);
321         pty = -1;
322     }
323 #endif  /* DKS */
324
325     if (!strncmp(ptyName, "/dev/", strlen("/dev/"))) {
326         ptyName = ptyName + strlen("/dev/");
327     }
328
329     c = XtMalloc(strlen(ptyName) + 1);
330     (void) strcpy(c, ptyName);
331
332     return(c);
333 }
334
335 static char *
336 UtmpEntryCreate(Widget w, pid_t pid, char *utmpLine)
337 {
338 #if !defined(CSRG_BASED) /* XXX */
339     DtTermPrimitiveWidget tw = (DtTermPrimitiveWidget) w;
340     struct termData *tpd = tw->term.tpd;
341     struct utmp ut;
342     struct utmp *utPtr;
343     char *c;
344     char *displayName;
345     time_t now;
346     Boolean closePty = False;
347 #ifdef  UT_HOST
348     char *seat;
349 #endif  /* UT_HOST */
350 #ifdef  UT_ADDR
351     struct sockaddr_in from;
352     int fromLen;
353 #endif  /* UT_ADDR */
354
355     /* initialize utmp stuff, just incase... */
356     (void) _DtTermPrimUtmpInit(w);
357
358     /* set up the entry to search for...
359      */
360
361     /* create the ut_line... */
362     (void) strncpy(ut.ut_line, utmpLine, sizeof(ut.ut_line));
363
364     ut.ut_type = DEAD_PROCESS;
365
366     /* position to entry in utmp file... */
367     (void) setutent();
368     if (NULL == (utPtr = getutline(&ut))) {
369         /* build a base utmp entry... */
370         utPtr = &ut;
371 #ifdef  __hpux
372         if (c = strstr(utmpLine, "tty")) {
373             c += strlen("tty");
374         } else if (c = strstr(utmpLine, "pts")) {
375             c += strlen("pts");
376         } else {
377             c = utmpLine;
378             if (strlen(utmpLine) > sizeof(utPtr->ut_id)) {
379                 c += strlen(utmpLine) - sizeof(utPtr->ut_id);
380             }
381         }
382         (void) strncpy(utPtr->ut_id, c, sizeof(utPtr->ut_id));
383 #else   /* __hpux */
384 #if defined(__AIX)
385         (void) strncpy(utPtr->ut_id, utmpLine,
386                 sizeof(utPtr->ut_id));
387 #else   /* __AIX */
388 #if defined(linux) || defined(sun)
389         if (c = strchr(utmpLine, '/')) {
390             c++;
391         } else {
392             c = utmpLine;
393         }
394         (void) strncpy(utPtr->ut_id, c, sizeof(utPtr->ut_id));
395 #else   /* linux || sun */
396         error out -- missing code for utPtr->ut_id
397 #endif  /* sun */
398 #endif  /* __AIX */
399 #endif  /* __hpux */
400     }
401
402     /* set up the new entry... */
403     utPtr->ut_type = USER_PROCESS;
404 #if !defined(linux)
405     utPtr->ut_exit.e_termination = 0;
406     utPtr->ut_exit.e_exit = 2;
407 #endif
408                 
409     snprintf(utPtr->ut_user, sizeof(utPtr->ut_user),
410              "%s", (userName && *userName) ? userName : "????");
411     (void) strncpy(utPtr->ut_line, utmpLine, sizeof(utPtr->ut_line));
412     utPtr->ut_pid = pid;
413     (void) time(&now);
414     utPtr->ut_time = now;
415
416     /* clear and set the host and addr fields... */
417 #ifdef  UT_HOST
418     (void) memset(utPtr->UT_HOST, '\0', sizeof(utPtr->UT_HOST));
419     /* stuff the display name into ut_host.  We will stuff as much as
420      * will fit dropping domain chunks as necessary to make it fit.  If
421      * we still can't fit with the entire domain removed, we will truncate
422      * the display name...
423      */
424     displayName = DisplayString(XtDisplay(w));
425     (void) strncpy(utPtr->UT_HOST, displayName, sizeof(utPtr->UT_HOST));
426 #ifdef  UT_HOST_LEN
427     utPtr->UT_HOST_LEN = strlen(displayName + 1);
428 #endif  /* UT_HOST_LEN */
429     if (sizeof(utPtr->UT_HOST) < strlen(displayName)) {
430         /* pull off the seat number... */
431         seat = strchr(displayName, ':');
432         /* back up through the display name.  Each time we hit a '.' that
433          * signals the start of a new domain chunk, see if we can fit
434          * the display name (less these domain chunks) plus the seat number
435          * into the structure...
436          */
437         if (seat - displayName < sizeof(utPtr->UT_HOST)) {
438             c = utPtr->UT_HOST + (seat - displayName) - 1;
439         } else {
440             c = utPtr->UT_HOST + sizeof(utPtr->UT_HOST) - 1;
441         }
442         for (; c >= utPtr->UT_HOST; c--) {
443             /* hit a '.'... */
444             if ((*c == '.') && (c - utPtr->UT_HOST + strlen(seat) <
445                     sizeof(utPtr->UT_HOST))) {
446                 /* everything left of the dot plus the seat will fit!... */
447                 break;
448             }
449         }
450         if (c >= utPtr->UT_HOST) {
451             /* we can perform a fit with some domains stripped... */
452             (void) strncpy(c, seat, sizeof(utPtr->UT_HOST) -
453                     (c - utPtr->UT_HOST));
454             if ((c - utPtr->UT_HOST) + strlen(seat) < sizeof(utPtr->UT_HOST)) {
455                 /* null out the end of the host name... */
456                 utPtr->UT_HOST[c - utPtr->UT_HOST + strlen(seat)] = '\0';
457 #ifdef  UT_HOST_LEN
458                 utPtr->UT_HOST_LEN = c - utPtr->UT_HOST + strlen(seat) + 1;
459 #endif  /* UT_HOST_LEN */
460             }
461         } else {
462             /* we can't fit even a single full chunk from the domain.
463              * since it is more important to get the seat number in (the
464              * host can be obtained from the addr), truncate the host.
465              */
466             (void) strncpy(utPtr->UT_HOST - strlen(seat), seat,
467                     strlen(seat));
468 #ifdef  UT_HOST_LEN
469             utPtr->UT_HOST_LEN = strlen(seat);
470 #endif  /* UT_HOST_LEN */
471         }
472     }
473 #endif  /* UT_HOST */
474
475 #ifdef  UT_ADDR
476     (void) memset(&utPtr->ut_addr, '\0', sizeof(utPtr->ut_addr));
477
478     /* get the canonical host of the X socket... */
479     fromLen = sizeof(from);
480     if (!getpeername(ConnectionNumber(XtDisplay(w)), &from, &fromLen) &&
481             (from.sin_family == AF_INET)) {
482         utPtr->ut_addr = from.sin_addr.s_addr;
483     }
484 #endif  /* UT_ADDR */
485
486     /* write it out... */
487     if (_pututline(utPtr)) {
488         /* success...
489          */
490         (void) endutent();
491         return(utmpLine);
492     }
493     /* failure... */
494     (void) endutent();
495     return((char *) 0);
496 #else /* __OpenBSD__ */
497     return(utmpLine);
498 #endif
499 }
500
501 /* this is a public wrapper around the previous function that runs the
502  * previous function setuid root...
503  */
504 char *
505 _DtTermPrimUtmpEntryCreate(Widget w, pid_t pid, char *utmpLine)
506 {
507     char *retValue;
508
509     /* this function needs to be suid root... */
510     (void) _DtTermPrimToggleSuidRoot(True);
511     retValue = UtmpEntryCreate(w, pid, utmpLine);
512     /* we now need to turn off setuid root... */
513     (void) _DtTermPrimToggleSuidRoot(False);
514
515     return(retValue);
516 }
517
518 static void
519 UtmpEntryDestroy(Widget w, char *utmpLine)
520 {
521 #if !defined(CSRG_BASED)
522     struct utmp ut;
523     struct utmp *utPtr;
524     time_t now;
525
526     ut.ut_type = USER_PROCESS;
527     snprintf(ut.ut_line, sizeof(ut.ut_line), "%s", utmpLine);
528     (void) setutent();
529     if (utPtr = getutline(&ut)) {
530         utPtr->ut_type = DEAD_PROCESS;
531 #if !defined(linux)
532         utPtr->ut_exit.e_termination = 0;
533         utPtr->ut_exit.e_exit = 0;
534 #endif
535         (void) time(&now);
536         utPtr->ut_time = now;
537 #ifdef  UT_HOST
538         (void) memset(utPtr->ut_host, '\0', sizeof(utPtr->ut_host));
539 #endif  /* UT_HOST */
540 #ifdef  UT_ADDR
541         (void) memset(&utPtr->ut_addr, '\0', sizeof(utPtr->ut_addr));
542 #endif  /* UT_ADDR */
543         (void) pututline(utPtr);
544     }
545     (void) endutent();
546
547     (void) DeleteUtmpInfo(utmpLine);
548 #endif /* !__OpenBSD__ */
549 }
550
551 /* this is a public wrapper around the previous function that runs the
552  * previous function setuid root...
553  */
554 void
555 _DtTermPrimUtmpEntryDestroy(Widget w, char *utmpLine)
556 {
557     /* this function needs to be suid root... */
558     (void) _DtTermPrimToggleSuidRoot(True);
559     (void) UtmpEntryDestroy(w, utmpLine);
560     /* we now need to turn off setuid root... */
561     (void) _DtTermPrimToggleSuidRoot(False);
562 }
563
564 void
565 _DtTermPrimUtmpCleanup()
566 {
567     DebugF('s', 10, fprintf(stderr, ">>_DtTermPrimUtmpCleanup() starting\n"));
568     _DtTermProcessLock();
569     while (utmpInfoHead->next && utmpInfoHead->next->utmpLine) {
570         DebugF('s', 10, fprintf(stderr, ">>resetting utmp for id \"%s\"\n",
571                 utmpInfoHead->next->utmpLine));
572         (void) _DtTermPrimUtmpEntryDestroy((Widget) 0,
573                 utmpInfoHead->next->utmpLine);
574     }
575     _DtTermProcessUnlock();
576     DebugF('s', 10, fprintf(stderr, ">>_DtTermPrimUtmpCleanup() finished\n"));
577 }