833a12108d123ae818137f614ba3c5a572425862
[vte.git] / gnome-pty-helper / gnome-utmp.c
1 /*
2  * utmp/wtmp file updating
3  *
4  * Authors:
5  *    Miguel de Icaza (miguel@gnu.org).
6  *    Timur I. Bakeyev (timur@gnu.org).
7  *
8  * FIXME: Do we want to register the PID of the process running *under* the
9  * subshell or the PID of the parent process? (we are doing the latter now).
10  *
11  * FIXME: Solaris (utmpx) stuff need to be checked.
12  */
13
14 #include <config.h>
15 #include <sys/types.h>
16 #include <sys/file.h>
17 #include <stdio.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <fcntl.h>
22 #include <pwd.h>
23 #include <errno.h>
24
25 #if defined(TIME_WITH_SYS_TIME)
26 #    include <sys/time.h>
27 #include <time.h>
28 #else
29 #  if defined(HAVE_SYS_TIME_H)
30 #    include <sys/time.h>
31 #  else
32 #    include <time.h>
33 #  endif
34 #endif
35
36 #if defined(HAVE_LASTLOG_H)
37 #    include <lastlog.h>
38 #endif
39
40 #if defined(HAVE_PATHS_H)
41 #    include <paths.h>
42 #endif
43
44 #if defined(HAVE_UTMP_H)
45 #    include <utmp.h>
46 #endif
47
48 #if defined(HAVE_UTMPX_H)
49 #    include <utmpx.h>
50 #endif
51
52 #if defined(HAVE_TTYENT_H)
53 #    include <ttyent.h>
54 #endif
55
56 #include "gnome-pty.h"
57 #include "gnome-login-support.h"
58
59
60
61 #if !defined(UTMP_OUTPUT_FILENAME)
62 #    if defined(UTMP_FILE)
63 #        define UTMP_OUTPUT_FILENAME UTMP_FILE
64 #    elif defined(_PATH_UTMP) /* BSD systems */
65 #        define UTMP_OUTPUT_FILENAME _PATH_UTMP
66 #    else
67 #        define UTMP_OUTPUT_FILENAME "/etc/utmp"
68 #    endif
69 #endif
70
71 #if !defined(WTMP_OUTPUT_FILENAME)
72 #    if defined(WTMPX_FILE)
73 #        define WTMP_OUTPUT_FILENAME WTMPX_FILE
74 #    elif defined(_PATH_WTMPX)
75 #        define WTMP_OUTPUT_FILENAME _PATH_WTMPX
76 #    elif defined(WTMPX_FILENAME)
77 #        define WTMP_OUTPUT_FILENAME WTMPX_FILENAME
78 #    elif defined(WTMP_FILE)
79 #        define WTMP_OUTPUT_FILENAME WTMP_FILE
80 #    elif defined(_PATH_WTMP) /* BSD systems */
81 #        define WTMP_OUTPUT_FILENAME _PATH_WTMP
82 #    else
83 #        define WTMP_OUTPUT_FILENAME "/etc/wtmp"
84 #    endif
85 #endif
86
87 #if defined(_PATH_LASTLOG) /* BSD systems */
88 #    define LASTLOG_OUTPUT_FILE _PATH_LASTLOG
89 #else
90 #    define LASTLOG_OUTPUT_FILE "/var/log/lastlog"
91 #endif
92
93 #if defined(HAVE_UPDWTMPX)
94 #include <utmpx.h>
95 #define update_wtmp updwtmpx
96 #elif defined(HAVE_UPDWTMP)
97 #define update_wtmp updwtmp
98 #else /* !HAVE_UPDWTMPX && !HAVE_UPDWTMP */
99 static void
100 update_wtmp (char *file, UTMP *putmp)
101 {
102         int fd, times = 3;
103 #if defined(HAVE_FCNTL)
104         struct flock lck;
105
106         lck.l_whence = SEEK_END;
107         lck.l_len    = 0;
108         lck.l_start  = 0;
109         lck.l_type   = F_WRLCK;
110 #endif
111
112         if ((fd = open (file, O_WRONLY|O_APPEND, 0)) < 0)
113                 return;
114
115 #if defined (HAVE_FCNTL)
116         while (times--)
117           if ((fcntl (fd, F_SETLK, &lck) < 0))
118             {
119               if (errno != EAGAIN && errno != EACCES) {
120                 close (fd);
121                 return;
122               }
123               sleep (1); /*?!*/
124             } else
125               break;
126 #elif defined(HAVE_FLOCK)
127         while (times--)
128           if (flock (fd, LOCK_EX | LOCK_NB) < 0)
129             {
130               if (errno != EWOULDBLOCK)
131                 {
132                   close (fd);
133                   return;
134                 }
135               sleep (1); /*?!*/
136             } else
137               break;
138 #endif /* HAVE_FLOCK */
139
140         lseek (fd, 0, SEEK_END);
141         write (fd, putmp, sizeof(UTMP));
142
143         /* unlock the file */
144 #if defined(HAVE_FCNTL)
145         lck.l_type = F_UNLCK;
146         fcntl (fd, F_SETLK, &lck);
147 #elif defined(HAVE_FLOCK)
148         flock (fd, LOCK_UN);
149 #endif
150         close (fd);
151 }
152 #endif /* !HAVE_GETUTMPX */
153
154
155 #if defined(HAVE_GETUTMPX)
156 static void
157 update_utmp (UTMP *ut)
158 {
159         setutxent();
160         pututxline (ut);
161 }
162 #elif defined(HAVE_GETUTENT)
163 static void
164 update_utmp (UTMP *ut)
165 {
166         setutent();
167         pututline (ut);
168 }
169 #elif defined(HAVE_GETTTYENT)
170 /* This variant is sutable for most BSD */
171 static void
172 update_utmp (UTMP *ut)
173 {
174         struct ttyent *ty;
175         int fd, pos = 0;
176
177         if ((fd = open (UTMP_OUTPUT_FILENAME, O_RDWR|O_CREAT, 0644)) < 0)
178                 return;
179
180         setttyent ();
181         while ((ty = getttyent ()) != NULL)
182         {
183                 ++pos;
184                 if (strncmp (ty->ty_name, ut->ut_line, sizeof (ut->ut_line)) == 0)
185                 {
186                         lseek (fd, (off_t)(pos * sizeof(UTMP)), SEEK_SET);
187                         write(fd, ut, sizeof(UTMP));
188                 }
189         }
190         endttyent ();
191
192         close(fd);
193 }
194 #else
195 #define update_utmp(ut)
196 #endif
197
198 #if !defined(HAVE_LASTLOG)
199 #define update_lastlog(login_name, ut)
200 #else
201 static void
202 update_lastlog(char* login_name, UTMP *ut)
203 {
204     struct passwd *pwd;
205     struct lastlog ll;
206     int fd;
207
208     if ((fd = open(LASTLOG_OUTPUT_FILE, O_WRONLY, 0)) < 0)
209         return;
210
211     if ((pwd=getpwnam(login_name)) == NULL)
212         return;
213
214     memset (&ll, 0, sizeof(ll));
215
216     lseek (fd, (off_t)pwd->pw_uid * sizeof (ll), SEEK_SET);
217
218     time (&ll.ll_time);
219
220     strncpy (ll.ll_line, ut->ut_line, sizeof (ll.ll_line));
221
222 #if defined(HAVE_UT_UT_HOST)
223     if (ut->ut_host)
224         strncpy (ll.ll_host, ut->ut_host, sizeof (ll.ll_host));
225 #endif
226
227     write (fd, (void *)&ll, sizeof (ll));
228     close (fd);
229 }
230 #endif /* HAVE_LASTLOG */
231
232 void
233 write_logout_record (void *data, int utmp, int wtmp)
234 {
235         UTMP put, *ut = data;
236
237         memset (&put, 0, sizeof(UTMP));
238
239 #if defined(HAVE_UT_UT_TYPE)
240         put.ut_type = DEAD_PROCESS;
241 #endif
242 #if defined(HAVE_UT_UT_ID)
243         strncpy (put.ut_id, ut->ut_id, sizeof (put.ut_id));
244 #endif
245
246         strncpy (put.ut_line, ut->ut_line, sizeof (put.ut_line));
247
248 #if defined(HAVE_UT_UT_TV)
249         gettimeofday ((struct timeval*) &put.ut_tv, NULL);
250 #elif defined(HAVE_UT_UT_TIME)
251         time (&put.ut_time);
252 #endif
253
254         if (utmp)
255                 update_utmp (&put);
256
257         if (wtmp)
258                 update_wtmp (WTMP_OUTPUT_FILENAME, &put);
259
260         free (ut);
261 }
262
263 void *
264 write_login_record (char *login_name, char *display_name,
265                     char *term_name, int utmp, int wtmp, int lastlog)
266 {
267         UTMP *ut;
268         char *pty = term_name;
269
270         if ((ut=(UTMP *) malloc (sizeof (UTMP))) == NULL)
271                 return NULL;
272
273         memset (ut, 0, sizeof (UTMP));
274
275 #if defined(HAVE_UT_UT_NAME)
276         strncpy (ut->ut_name, login_name, sizeof (ut->ut_name));
277 #elif defined(HAVE_UT_UT_USER)
278         strncpy (ut->ut_user, login_name, sizeof (ut->ut_user));
279 #endif
280
281         /* This shouldn't happen */
282         if (strncmp (pty, "/dev/", 5) == 0)
283             pty += 5;
284
285 #if defined(HAVE_STRRCHR)
286         {
287                 char *p;
288
289                 if (strncmp (pty, "pts", 3) &&
290                     (p = strrchr (pty, '/')) != NULL)
291                         pty = p + 1;
292         }
293 #endif
294
295 #if defined(HAVE_UT_UT_ID)
296         /* Just a safe-guard */
297         ut->ut_id [0] = '\0';
298
299         /* BSD-like terminal name */
300         if (strncmp (pty, "pts", 3) == 0 ||
301             strncmp (pty, "pty", 3) == 0 ||
302             strncmp (pty, "tty", 3) == 0) {
303                 strncpy (ut->ut_id, pty+3, sizeof (ut->ut_id));
304         } else {
305                 unsigned int num;
306                 char buf[10];
307                 /* Try to get device number and convert it to gnome-terminal # */
308                 if (sscanf (pty, "%*[^0-9a-f]%x", &num) == 1) {
309                         sprintf (buf, "gt%2.2x", num);
310                         strncpy (ut->ut_id, buf, sizeof (ut->ut_id));
311                 }
312         }
313 #endif
314
315         /* For utmpx ut_line should be null terminated */
316         /* We do that for both cases to be sure */
317         strncpy (ut->ut_line, pty, sizeof (ut->ut_line));
318         ut->ut_line[sizeof (ut->ut_line)-1] = '\0';
319
320         /* We want parent's pid, not our own */
321 #if defined(HAVE_UT_UT_PID)
322         ut->ut_pid  = getppid ();
323 #endif
324
325 #if defined(HAVE_UT_UT_TYPE)
326         ut->ut_type = USER_PROCESS;
327 #endif
328         /* If structure has ut_tv it doesn't need ut_time */
329 #if defined(HAVE_UT_UT_TV)
330         gettimeofday ((struct timeval*) &(ut->ut_tv), NULL);
331 #elif defined(HAVE_UT_UT_TIME)
332         time (&ut->ut_time);
333 #endif
334         /* ut_ host supposed to be null terminated or len should */
335         /* be specifid in additional field. We do both :)  */
336 #if defined(HAVE_UT_UT_HOST)
337         strncpy (ut->ut_host, display_name, sizeof (ut->ut_host));
338         ut->ut_host [sizeof (ut->ut_host)-1] = '\0';
339 #    if defined(HAVE_UT_UT_SYSLEN)
340         ut->ut_syslen = strlen (ut->ut_host);
341 #    endif
342 #endif
343         if (utmp)
344                 update_utmp (ut);
345
346         if (wtmp)
347                 update_wtmp (WTMP_OUTPUT_FILENAME, ut);
348
349         if (lastlog)
350                 update_lastlog(login_name, ut);
351
352         return ut;
353 }
354
355 void *
356 update_dbs (int utmp, int wtmp, int lastlog, char *login_name, char *display_name, char *term_name)
357 {
358         return write_login_record (login_name, display_name,
359                                    term_name, utmp, wtmp, lastlog);
360 }