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
update_wtmp(char * file,UTMP * putmp)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) || defined(HAVE_GETUTXID)
156 static void
update_utmp(UTMP * ut)157 update_utmp (UTMP *ut)
158 {
159 	setutxent();
160 	pututxline (ut);
161 	endutxent();
162 }
163 #elif defined(HAVE_GETUTENT)
164 static void
update_utmp(UTMP * ut)165 update_utmp (UTMP *ut)
166 {
167 	setutent();
168 	pututline (ut);
169 	endutent();
170 }
171 #elif defined(HAVE_GETTTYENT)
172 /* This variant is sutable for most BSD */
173 static void
update_utmp(UTMP * ut)174 update_utmp (UTMP *ut)
175 {
176 	struct ttyent *ty;
177 	int fd, pos = 0;
178 
179 	if ((fd = open (UTMP_OUTPUT_FILENAME, O_RDWR|O_CREAT, 0644)) < 0)
180 		return;
181 
182 	setttyent ();
183 	while ((ty = getttyent ()) != NULL)
184 	{
185 		++pos;
186 		if (strncmp (ty->ty_name, ut->ut_line, sizeof (ut->ut_line)) == 0)
187 		{
188 			lseek (fd, (off_t)(pos * sizeof(UTMP)), SEEK_SET);
189 			write(fd, ut, sizeof(UTMP));
190 		}
191 	}
192 	endttyent ();
193 
194 	close(fd);
195 }
196 #else
197 #define update_utmp(ut)
198 #endif
199 
200 #if !defined(HAVE_LASTLOG)
201 #define update_lastlog(login_name, ut)
202 #else
203 static void
update_lastlog(char * login_name,UTMP * ut)204 update_lastlog(char* login_name, UTMP *ut)
205 {
206     struct passwd *pwd;
207     struct lastlog ll;
208     int fd;
209 
210     if ((fd = open(LASTLOG_OUTPUT_FILE, O_WRONLY, 0)) < 0)
211 	return;
212 
213     if ((pwd=getpwnam(login_name)) == NULL)
214 	return;
215 
216     memset (&ll, 0, sizeof(ll));
217 
218     lseek (fd, (off_t)pwd->pw_uid * sizeof (ll), SEEK_SET);
219 
220     time (&ll.ll_time);
221 
222     strncpy (ll.ll_line, ut->ut_line, sizeof (ll.ll_line));
223 
224 #if defined(HAVE_UT_UT_HOST)
225     if (ut->ut_host)
226 	strncpy (ll.ll_host, ut->ut_host, sizeof (ll.ll_host));
227 #endif
228 
229     write (fd, (void *)&ll, sizeof (ll));
230     close (fd);
231 }
232 #endif /* HAVE_LASTLOG */
233 
234 void
write_logout_record(char * login_name,void * data,int utmp,int wtmp)235 write_logout_record (char *login_name, void *data, int utmp, int wtmp)
236 {
237 	UTMP put, *ut = data;
238 	struct timeval tv;
239 
240 	memset (&put, 0, sizeof(UTMP));
241 
242 #if defined(HAVE_UT_UT_TYPE)
243 	put.ut_type = DEAD_PROCESS;
244 #endif
245 #if defined(HAVE_UT_UT_ID)
246 	strncpy (put.ut_id, ut->ut_id, sizeof (put.ut_id));
247 #endif
248 
249 	strncpy (put.ut_line, ut->ut_line, sizeof (put.ut_line));
250 
251 #if defined(HAVE_UT_UT_TV)
252 	gettimeofday(&tv, NULL);
253 	put.ut_tv.tv_sec = tv.tv_sec;
254 	put.ut_tv.tv_usec = tv.tv_usec;
255 #elif defined(HAVE_UT_UT_TIME)
256 	time (&put.ut_time);
257 #endif
258 
259 #if defined(HAVE_UT_UT_NAME)
260 	strncpy (put.ut_name, login_name, sizeof (put.ut_name));
261 #elif defined(HAVE_UT_UT_USER)
262 	strncpy (put.ut_user, login_name, sizeof (put.ut_user));
263 #endif
264 
265 	if (utmp)
266 		update_utmp (&put);
267 
268 	if (wtmp)
269 		update_wtmp (WTMP_OUTPUT_FILENAME, &put);
270 
271 	free (ut);
272 }
273 
274 void *
write_login_record(char * login_name,char * display_name,char * term_name,int utmp,int wtmp,int lastlog)275 write_login_record (char *login_name, char *display_name,
276 		    char *term_name, int utmp, int wtmp, int lastlog)
277 {
278 	UTMP *ut;
279 	char *pty = term_name;
280 	struct timeval tv;
281 
282 	if ((ut=(UTMP *) malloc (sizeof (UTMP))) == NULL)
283 		return NULL;
284 
285 	memset (ut, 0, sizeof (UTMP));
286 
287 #if defined(HAVE_UT_UT_NAME)
288 	strncpy (ut->ut_name, login_name, sizeof (ut->ut_name));
289 #elif defined(HAVE_UT_UT_USER)
290 	strncpy (ut->ut_user, login_name, sizeof (ut->ut_user));
291 #endif
292 
293 	/* This shouldn't happen */
294 	if (strncmp (pty, "/dev/", 5) == 0)
295 	    pty += 5;
296 
297 #if defined(HAVE_STRRCHR)
298 	{
299 		char *p;
300 
301 		if (strncmp (pty, "pts", 3) &&
302 		    (p = strrchr (pty, '/')) != NULL)
303 			pty = p + 1;
304 	}
305 #endif
306 
307 #if defined(HAVE_UT_UT_ID)
308 	/* Just a safe-guard */
309 	ut->ut_id [0] = '\0';
310 
311 	/* BSD-like terminal name */
312 	if (strncmp (pty, "pts", 3) == 0 ||
313 	    strncmp (pty, "pty", 3) == 0 ||
314 	    strncmp (pty, "tty", 3) == 0) {
315 		strncpy (ut->ut_id, pty+3, sizeof (ut->ut_id));
316 	} else {
317 		unsigned int num;
318 		char buf[10];
319 		/* Try to get device number and convert it to gnome-terminal # */
320 		if (sscanf (pty, "%*[^0-9a-f]%x", &num) == 1) {
321 			sprintf (buf, "gt%2.2x", num);
322 			strncpy (ut->ut_id, buf, sizeof (ut->ut_id));
323 		}
324 	}
325 #endif
326 
327 	/* For utmpx ut_line should be null terminated */
328 	/* We do that for both cases to be sure */
329 	strncpy (ut->ut_line, pty, sizeof (ut->ut_line));
330 	ut->ut_line[sizeof (ut->ut_line)-1] = '\0';
331 
332 	/* We want parent's pid, not our own */
333 #if defined(HAVE_UT_UT_PID)
334 	ut->ut_pid  = getppid ();
335 #endif
336 
337 #if defined(HAVE_UT_UT_TYPE)
338 	ut->ut_type = USER_PROCESS;
339 #endif
340 	/* If structure has ut_tv it doesn't need ut_time */
341 #if defined(HAVE_UT_UT_TV)
342 	gettimeofday(&tv, NULL);
343 	ut->ut_tv.tv_sec = tv.tv_sec;
344 	ut->ut_tv.tv_usec = tv.tv_usec;
345 #elif defined(HAVE_UT_UT_TIME)
346 	time (&ut->ut_time);
347 #endif
348 	/* ut_ host supposed to be null terminated or len should */
349 	/* be specifid in additional field. We do both :)  */
350 #if defined(HAVE_UT_UT_HOST)
351 	strncpy (ut->ut_host, display_name, sizeof (ut->ut_host));
352 	ut->ut_host [sizeof (ut->ut_host)-1] = '\0';
353 #    if defined(HAVE_UT_UT_SYSLEN)
354 	ut->ut_syslen = strlen (ut->ut_host);
355 #    endif
356 #endif
357 	if (utmp)
358 		update_utmp (ut);
359 
360 	if (wtmp)
361 		update_wtmp (WTMP_OUTPUT_FILENAME, ut);
362 
363 	if (lastlog)
364 		update_lastlog(login_name, ut);
365 
366 	return ut;
367 }
368 
369 void *
update_dbs(int utmp,int wtmp,int lastlog,char * login_name,char * display_name,char * term_name)370 update_dbs (int utmp, int wtmp, int lastlog, char *login_name, char *display_name, char *term_name)
371 {
372 	return write_login_record (login_name, display_name,
373 				   term_name, utmp, wtmp, lastlog);
374 }
375