*** empty log message ***
[dana/urxvt.git] / src / logging.C
1 /*--------------------------------*-C-*---------------------------------*
2  * File:        logging.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1992      John Bovey <jdb@ukc.ac.uk>
7  *                              - original version
8  * Copyright (c) 1993      lipka
9  * Copyright (c) 1993      Brian Stempien <stempien@cs.wmich.edu>
10  * Copyright (c) 1995      Raul Garcia Garcia <rgg@tid.es>
11  * Copyright (c) 1995      Piet W. Plomp <piet@idefix.icce.rug.nl>
12  * Copyright (c) 1997      Raul Garcia Garcia <rgg@tid.es>
13  * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com>
14  *                              - extensive modifications
15  * Copyright (c) 1999      D J Hawkey Jr <hawkeyd@visi.com>
16  *                              - lastlog support
17  * Copyright (c) 2004-2006 Marc Lehmann <pcg@goof.com>
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32  *----------------------------------------------------------------------*/
33
34 #include "../config.h"
35 #include "rxvt.h"
36 #include "ptytty.h"
37
38 #if UTMP_SUPPORT
39
40 #if HAVE_STRUCT_UTMP
41 static int              rxvt_write_bsd_utmp              (int utmp_pos, struct utmp *wu);
42 static void             rxvt_update_wtmp                 (const char *fname, const struct utmp *putmp);
43 #endif
44
45 static void             rxvt_update_lastlog              (const char *fname, const char *pty, const char *host);
46
47 /*
48  * BSD style utmp entry
49  *      ut_line, ut_name, ut_host, ut_time
50  * SYSV style utmp (and utmpx) entry
51  *      ut_user, ut_id, ut_line, ut_pid, ut_type, ut_exit, ut_time
52  */
53
54 /* ------------------------------------------------------------------------- */
55
56 /*
57  * make and write utmp and wtmp entries
58  */
59 void
60 rxvt_ptytty_unix::login (int cmd_pid, bool login_shell, const char *hostname)
61 {
62   const char *pty = name;
63
64   if (!pty || !*pty)
65     return;
66
67   this->cmd_pid     = cmd_pid;
68   this->login_shell = login_shell;
69
70 #ifdef HAVE_STRUCT_UTMP
71   struct utmp *ut = &this->ut;
72 #endif
73 #ifdef HAVE_STRUCT_UTMPX
74   struct utmpx *utx = &this->utx;
75 #endif
76   int i;
77   struct passwd *pwent = getpwuid (getuid ());
78
79   if (!strncmp (pty, "/dev/", 5))
80     pty += 5;           /* skip /dev/ prefix */
81
82 #if defined(HAVE_UTMP_PID) || defined(HAVE_STRUCT_UTMPX)
83   if (!strncmp (pty, "pty", 3) || !strncmp (pty, "tty", 3))
84     strncpy (ut_id, pty + 3, sizeof (ut_id));
85   else if (sscanf (pty, "pts/%d", &i) == 1)
86     sprintf (ut_id, "vt%02x", (i & 0xff));      /* sysv naming */
87   else if (strncmp (pty, "pty", 3) && strncmp (pty, "tty", 3))
88     {
89       rxvt_warn ("can't parse tty name \"%s\", not adding utmp entry.\n", pty);
90       return;
91     }
92 #endif
93
94 #ifdef HAVE_STRUCT_UTMP
95   memset (ut, 0, sizeof (struct utmp));
96 # ifdef HAVE_UTMP_PID
97   setutent ();
98   strncpy (ut->ut_id, ut_id, sizeof (ut->ut_id));
99   ut->ut_type = DEAD_PROCESS;
100   getutid (ut);         /* position to entry in utmp file */
101   strncpy (this->ut_id, ut_id, sizeof (this->ut_id));
102 # endif
103 #endif
104
105 #ifdef HAVE_STRUCT_UTMPX
106   memset (utx, 0, sizeof (struct utmpx));
107   setutxent ();
108   strncpy (utx->ut_id, ut_id, sizeof (utx->ut_id));
109   utx->ut_type = DEAD_PROCESS;
110   getutxid (utx);               /* position to entry in utmp file */
111   strncpy (this->ut_id, ut_id, sizeof (this->ut_id));
112 #endif
113
114 #ifdef HAVE_STRUCT_UTMP
115   strncpy (ut->ut_line, pty, sizeof (ut->ut_line));
116   ut->ut_time = time (NULL);
117 # ifdef HAVE_UTMP_PID
118   strncpy (ut->ut_user, (pwent && pwent->pw_name) ? pwent->pw_name : "?",
119           sizeof (ut->ut_user));
120   strncpy (ut->ut_id, ut_id, sizeof (ut->ut_id));
121   ut->ut_time = time (NULL);
122   ut->ut_pid = cmd_pid;
123 #  ifdef HAVE_UTMP_HOST
124   strncpy (ut->ut_host, hostname, sizeof (ut->ut_host));
125 #  endif
126   ut->ut_type = USER_PROCESS;
127   pututline (ut);
128   endutent ();                  /* close the file */
129   utmp_pos = 0;
130 # else
131   strncpy (ut->ut_name, (pwent && pwent->pw_name) ? pwent->pw_name : "?",
132           sizeof (ut->ut_name));
133 #  ifdef HAVE_UTMP_HOST
134   strncpy (ut->ut_host, hostname, sizeof (ut->ut_host));
135 #  endif
136 # endif
137 #endif
138
139 #ifdef HAVE_STRUCT_UTMPX
140   strncpy (utx->ut_line, pty, sizeof (utx->ut_line));
141   strncpy (utx->ut_user, (pwent && pwent->pw_name) ? pwent->pw_name : "?",
142           sizeof (utx->ut_user));
143   strncpy (utx->ut_id, ut_id, sizeof (utx->ut_id));
144 # if HAVE_UTMPX_SESSION
145   utx->ut_session = getsid (0);
146 # endif
147   utx->ut_tv.tv_sec = time (NULL);
148   utx->ut_tv.tv_usec = 0;
149   utx->ut_pid = cmd_pid;
150 # ifdef HAVE_UTMPX_HOST
151   strncpy (utx->ut_host, hostname, sizeof (utx->ut_host));
152 #  if 0
153   {
154     char           *colon;
155
156     if ((colon = strrchr (ut->ut_host, ':')) != NULL)
157       *colon = '\0';
158   }
159 #  endif
160 # endif
161   utx->ut_type = USER_PROCESS;
162   pututxline (utx);
163   endutxent ();         /* close the file */
164   utmp_pos = 0;
165 #endif
166
167 #if defined(HAVE_STRUCT_UTMP) && !defined(HAVE_UTMP_PID)
168   {
169 # ifdef HAVE_TTYSLOT
170     int fdstdin = dup (STDIN_FILENO);
171     dup2 (pty->tty, STDIN_FILENO);
172
173     i = ttyslot ();
174     if (rxvt_write_bsd_utmp (i, ut))
175       utmp_pos = i;
176
177     dup2 (fdstdin, STDIN_FILENO);
178     close (fdstdin);
179 # else
180     FILE *fd0;
181
182     if ((fd0 = fopen (TTYTAB_FILENAME, "r")) != NULL)
183       {
184         char buf[256], name[256];
185
186         buf[sizeof (buf) - 1] = '\0';
187         for (i = 1; (fgets (buf, sizeof (buf) - 1, fd0) != NULL);)
188           {
189             if (*buf == '#' || sscanf (buf, "%s", name) != 1)
190               continue;
191             if (!strcmp (ut->ut_line, name))
192               {
193                 if (!rxvt_write_bsd_utmp (i, ut))
194                   i = 0;
195                 utmp_pos = i;
196                 fclose (fd0);
197                 break;
198               }
199             i++;
200           }
201         fclose (fd0);
202       }
203 # endif
204   }
205 #endif
206
207 #ifdef WTMP_SUPPORT
208 # ifdef WTMP_ONLY_ON_LOGIN
209   if (login_shell)
210 # endif
211     {
212 # ifdef HAVE_STRUCT_UTMP
213 #  ifdef HAVE_UPDWTMP
214       updwtmp (RXVT_WTMP_FILE, ut);
215 #  else
216       rxvt_update_wtmp (RXVT_WTMP_FILE, ut);
217 #  endif
218 # endif
219 # if defined(HAVE_STRUCT_UTMPX) && defined(HAVE_UPDWTMPX)
220       updwtmpx (RXVT_WTMPX_FILE, utx);
221 # endif
222     }
223 #endif
224 #if defined(LASTLOG_SUPPORT) && defined(RXVT_LASTLOG_FILE)
225   if (login_shell)
226     rxvt_update_lastlog (RXVT_LASTLOG_FILE, pty, hostname);
227 #endif
228 }
229
230 /* ------------------------------------------------------------------------- */
231 /*
232  * remove utmp and wtmp entries
233  */
234 void
235 rxvt_ptytty_unix::logout ()
236 {
237   if (!cmd_pid)
238     return;
239
240 #ifdef HAVE_STRUCT_UTMP
241   struct utmp *tmput, *ut = &this->ut;
242 #endif
243 #ifdef HAVE_STRUCT_UTMPX
244   struct utmpx *tmputx, *utx = &this->utx;
245 #endif
246
247 #ifdef HAVE_STRUCT_UTMP
248 # ifdef HAVE_UTMP_PID
249   memset (ut, 0, sizeof (struct utmp));
250   setutent ();
251   strncpy (ut->ut_id, this->ut_id, sizeof (ut->ut_id));
252   ut->ut_type = USER_PROCESS;
253   if ((tmput = getutid (ut)))           /* position to entry in utmp file */
254     ut = tmput;
255   ut->ut_type = DEAD_PROCESS;
256 # else
257   memset (ut->ut_name, 0, sizeof (ut->ut_name));
258 #  ifdef HAVE_UTMP_HOST
259   memset (ut->ut_host, 0, sizeof (ut->ut_host));
260 #  endif
261 # endif
262   ut->ut_time = time (NULL);
263 #endif
264
265 #ifdef HAVE_STRUCT_UTMPX
266   memset (utx, 0, sizeof (struct utmpx));
267   setutxent ();
268   strncpy (utx->ut_id, this->ut_id, sizeof (utx->ut_id));
269   utx->ut_type = USER_PROCESS;
270   if ((tmputx = getutxid (utx)))        /* position to entry in utmp file */
271     utx = tmputx;
272   utx->ut_type = DEAD_PROCESS;
273 # if HAVE_UTMPX_SESSION
274   utx->ut_session = getsid (0);
275 # endif
276   utx->ut_tv.tv_sec = time (NULL);
277   utx->ut_tv.tv_usec = 0;
278 #endif
279
280   /*
281    * Write ending wtmp entry
282    */
283 #ifdef WTMP_SUPPORT
284 # ifdef WTMP_ONLY_ON_LOGIN
285   if (login_shell)
286 # endif
287     {
288 # ifdef HAVE_STRUCT_UTMP
289 #  ifdef HAVE_UPDWTMP
290       updwtmp (RXVT_WTMP_FILE, ut);
291 #  else
292       rxvt_update_wtmp (RXVT_WTMP_FILE, ut);
293 #  endif
294 # endif
295 # if defined(HAVE_STRUCT_UTMPX) && defined(HAVE_UPDWTMPX)
296       updwtmpx (RXVT_WTMPX_FILE, utx);
297 # endif
298     }
299 #endif
300
301   /*
302    * Write utmp entry
303    */
304 #ifdef HAVE_STRUCT_UTMP
305 # ifdef HAVE_UTMP_PID
306   if (ut->ut_pid == cmd_pid)
307     pututline (ut);
308   endutent ();
309 # else
310   memset (ut, 0, sizeof (struct utmp));
311   rxvt_write_bsd_utmp (utmp_pos, ut);
312 # endif
313 #endif
314 #ifdef HAVE_STRUCT_UTMPX
315   if (utx->ut_pid == cmd_pid)
316     pututxline (utx);
317   endutxent ();
318 #endif
319
320   cmd_pid = 0;
321 }
322
323 /* ------------------------------------------------------------------------- */
324 /*
325  * Write a BSD style utmp entry
326  */
327 #if defined(HAVE_STRUCT_UTMP) && !defined(HAVE_UTMP_PID)
328 static int
329 rxvt_write_bsd_utmp (int utmp_pos, struct utmp *wu)
330 {
331   int             fd;
332
333   if (utmp_pos <= 0 || (fd = open (RXVT_UTMP_FILE, O_WRONLY)) == -1)
334     return 0;
335
336   if (lseek (fd, (off_t) (utmp_pos * sizeof (struct utmp)), SEEK_SET) != -1)
337     write (fd, wu, sizeof (struct utmp));
338   close (fd);
339   return 1;
340 }
341 #endif
342
343 /* ------------------------------------------------------------------------- */
344 /*
345  * Update a BSD style wtmp entry
346  */
347 #if defined(WTMP_SUPPORT) && !defined(HAVE_UPDWTMP) && defined(HAVE_STRUCT_UTMP)
348 static void
349 rxvt_update_wtmp (const char *fname, const struct utmp *putmp)
350 {
351   int             fd, gotlock, retry;
352   struct flock    lck;  /* fcntl locking scheme */
353   struct stat     sbuf;
354
355   if ((fd = open (fname, O_WRONLY | O_APPEND, 0)) < 0)
356     return;
357
358   lck.l_whence = SEEK_END;      /* start lock at current eof */
359   lck.l_len = 0;                /* end at ``largest possible eof'' */
360   lck.l_start = 0;
361   lck.l_type = F_WRLCK; /* we want a write lock */
362
363   /* attempt lock with F_SETLK; F_SETLKW would cause a deadlock! */
364   for (retry = 10, gotlock = 0; retry--;)
365     if (fcntl (fd, F_SETLK, &lck) != -1)
366       {
367         gotlock = 1;
368         break;
369       }
370     else if (errno != EAGAIN && errno != EACCES)
371       break;
372   if (!gotlock)
373     {           /* give it up */
374       close (fd);
375       return;
376     }
377   if (fstat (fd, &sbuf) == 0)
378     if (write (fd, putmp, sizeof (struct utmp)) != sizeof (struct utmp))
379       ftruncate (fd, sbuf.st_size);     /* remove bad writes */
380
381   lck.l_type = F_UNLCK; /* unlocking the file */
382   fcntl (fd, F_SETLK, &lck);
383   close (fd);
384 }
385 #endif
386
387 /* ------------------------------------------------------------------------- */
388 #ifdef LASTLOG_SUPPORT
389 static void
390 rxvt_update_lastlog (const char *fname, const char *pty, const char *host)
391 {
392 # ifdef HAVE_STRUCT_LASTLOGX
393   struct lastlogx llx;
394 # endif
395 # ifdef HAVE_STRUCT_LASTLOG
396   int             fd;
397   struct lastlog  ll;
398 #  ifdef LASTLOG_IS_DIR
399   char            lastlogfile[256];
400 #  endif
401   struct passwd  *pwent;
402 # endif
403
404 # ifdef HAVE_STRUCT_LASTLOGX
405   memset (&llx, 0, sizeof (llx));
406   llx.ll_tv.tv_sec = time (NULL);
407   llx.ll_tv.tv_usec = 0;
408   strncpy (llx.ll_line, pty, sizeof (llx.ll_line));
409   strncpy (llx.ll_host, host, sizeof (llx.ll_host));
410   updlastlogx (RXVT_LASTLOGX_FILE, getuid (), &llx);
411 # endif
412
413 # ifdef HAVE_STRUCT_LASTLOG
414   pwent = getpwuid (getuid ());
415   if (!pwent)
416     {
417       rxvt_warn ("no entry in password file, not updating lastlog.\n");
418       return;
419     }
420
421   memset (&ll, 0, sizeof (ll));
422   ll.ll_time = time (NULL);
423   strncpy (ll.ll_line, pty, sizeof (ll.ll_line));
424   strncpy (ll.ll_host, host, sizeof (ll.ll_host));
425 #  ifdef LASTLOG_IS_DIR
426   sprintf (lastlogfile, "%.*s/%.*s",
427           sizeof (lastlogfile) - sizeof (pwent->pw_name) - 2, fname,
428           sizeof (pwent->pw_name),
429           (!pwent->pw_name || pwent->pw_name[0] == '\0') ? "unknown"
430           : pwent->pw_name);
431   if ((fd = open (lastlogfile, O_WRONLY | O_CREAT, 0644)) >= 0)
432     {
433       write (fd, &ll, sizeof (ll));
434       close (fd);
435     }
436 #  else
437   if ((fd = open (fname, O_RDWR)) != -1)
438     {
439       if (lseek (fd, (off_t) ((long)pwent->pw_uid * sizeof (ll)),
440                 SEEK_SET) != -1)
441         write (fd, &ll, sizeof (ll));
442       close (fd);
443     }
444 #  endif                        /* LASTLOG_IS_DIR */
445 # endif                         /* HAVE_STRUCT_LASTLOG */
446 }
447 #endif                          /* LASTLOG_SUPPORT */
448 /* ------------------------------------------------------------------------- */
449
450 #endif                          /* UTMP_SUPPORT */