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