1 /*
2  * Copyright (C) 1997-2009, Michael Jennings
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 static const char cvs_ident[] = "$Id: utmp.c 51650 2010-08-26 01:34:13Z lucas $";
25 
26 #include "config.h"
27 #include "feature.h"
28 
29 # include "eterm_utmp.h"
30 # include "command.h"
31 # include "screen.h"
32 
33 #if defined(UTMP_SUPPORT) && !defined(HAVE_LIBUTEMPTER)
34 
35 /* screen.h includes config.h again, so re-fix these.  Pointed out by Sung-Hyun Nam <namsh@lgic.co.kr> */
36 # if defined(_HPUX_SOURCE) || defined(_AIX) || ((__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1))
37 #   undef HAVE_UTMPX_H
38 # endif
39 
40 /* don't go off end of ut_id & remember if an entry has been made */
41 #  if defined(USE_SYSV_UTMP) || defined(__OpenBSD__)
42 static char ut_id[5];           /* remember if entry to utmp made */
43 #  elif !defined(NEW_BSD_UTMP)
44 static int utmp_pos;            /* BSD position of utmp-stamp */
45 #  endif
46 
47 # ifdef USE_SYSV_UTMP
48 
49 #  ifdef HAVE_UTMPX_H
50 #   undef WTMP_FILENAME
51 #   define WTMP_FILENAME WTMPX_FILE
52 #   define update_wtmp updwtmpx
53 #  else /* HAVE_UTMPX_H */
54 
55 static void
update_wtmp(char * fname,struct utmp * putmp)56 update_wtmp(char *fname, struct utmp *putmp)
57 {
58 
59     int fd, retry = 10;         /* 10 attempts at locking */
60     struct flock lck;           /* fcntl locking scheme */
61 
62     if ((fd = open(fname, O_WRONLY | O_APPEND, 0)) < 0) {
63         D_UTMP(("Warning:  Unable to open \"%s\" for writing -- %s\n", fname, strerror(errno)));
64         return;
65     }
66     lck.l_whence = SEEK_END;    /* start lock at current eof */
67     lck.l_len = 0;              /* end at ``largest possible eof'' */
68     lck.l_start = 0;
69     lck.l_type = F_WRLCK;       /* we want a write lock */
70 
71     /* attempt lock with F_SETLK - F_SETLKW would cause a deadlock! */
72     while (retry--) {
73         if ((fcntl(fd, F_SETLK, &lck) < 0) && errno != EACCESS) {
74             D_UTMP(("Warning:  Unable to establish file lock on \"%s\" -- %s\n", fname, strerror(errno)));
75             close(fd);
76             return;             /* failed for unknown reason: give up */
77         } else if (errno == EACCESS) {
78             D_UTMP(("Warning:  Unable to establish file lock on \"%s\" -- %s\n", fname, strerror(errno)));
79         }
80     }
81 
82     write(fd, putmp, sizeof(struct utmp));
83 
84     /* unlocking the file */
85     lck.l_type = F_UNLCK;
86     fcntl(fd, F_SETLK, &lck);
87 
88     close(fd);
89 }
90 #  endif /* HAVE_UTMPX_H */
91 
92 void
add_utmp_entry(const char * pty,const char * hostname,int fd)93 add_utmp_entry(const char *pty, const char *hostname, int fd)
94 {
95     struct passwd *pwent = getpwuid(my_ruid);
96 
97 #   ifdef HAVE_UTMPX_H
98     struct utmpx utmp;
99     struct utmp utmp2;
100     MEMSET(&utmp, 0, sizeof(struct utmpx));
101 #   else
102     struct utmp utmp;
103     MEMSET(&utmp, 0, sizeof(struct utmp));
104 #   endif
105 
106 #   ifdef WITH_DMALLOC
107     return;
108 #   endif
109 
110     if (!strncmp(pty, "/dev/", 5))
111         pty += 5;               /* skip /dev/ prefix */
112     if (!strncmp(pty, "pty", 3) || !strncmp(pty, "tty", 3))
113         strncpy(ut_id, (pty + 3), sizeof(ut_id));       /* bsd naming */
114     else {
115         int n;
116 
117         if (sscanf(pty, "pts/%d", &n) == 1)
118             sprintf(ut_id, "vt%02x", n);        /* sysv naming */
119         else {
120             libast_print_error("can't parse tty name \"%s\"\n", pty);
121             ut_id[0] = '\0';    /* entry not made */
122             return;
123         }
124     }
125     strncpy(utmp.ut_id, ut_id, sizeof(utmp.ut_id));
126     utmp.ut_type = DEAD_PROCESS;
127 
128     privileges(INVOKE);
129 #   ifdef HAVE_UTMPX_H
130     getutmp(&utmp, &utmp2);
131     getutid(&utmp2);            /* position to entry in utmp file */
132 #   else
133     getutid(&utmp);             /* position to entry in utmp file */
134 #   endif
135 
136     /* set up the new entry */
137     strncpy(utmp.ut_id, ut_id, sizeof(utmp.ut_id));
138     strncpy(utmp.ut_line, pty, sizeof(utmp.ut_line));
139     strncpy(utmp.ut_name, pwent->pw_name, sizeof(utmp.ut_name));
140     strncpy(utmp.ut_user, pwent->pw_name, sizeof(utmp.ut_user));
141     strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
142     utmp.ut_type = USER_PROCESS;
143     utmp.ut_pid = getpid();
144 #   ifdef HAVE_UTMPX_H
145     utmp.ut_session = getsid(0);
146     utmp.ut_xtime = time(NULL);
147     utmp.ut_tv.tv_usec = 0;
148 #   else
149     utmp.ut_time = time(NULL);
150 #   endif
151 
152     /*
153      * write a utmp entry to the utmp file
154      */
155     utmpname(UTMP_FILENAME);
156 #   ifdef HAVE_UTMPX_H
157     getutmp(&utmp, &utmp2);
158     pututline(&utmp2);
159     pututxline(&utmp);
160 #   else
161     pututline(&utmp);
162 #   endif
163     update_wtmp(WTMP_FILENAME, &utmp);
164     endutent();                 /* close the file */
165     privileges(REVERT);
166     return;
167     fd = 0;
168 }
169 
170 void
remove_utmp_entry(void)171 remove_utmp_entry(void)
172 {
173 #   ifdef HAVE_UTMPX_H
174     struct utmp utmp;
175     struct utmpx utmpx;
176 
177     if (!ut_id[0])
178         return;                 /* entry not made */
179 
180     utmpname(UTMP_FILENAME);
181     setutent();
182     strncpy(utmp.ut_id, ut_id, sizeof(utmp.ut_id));
183     utmp.ut_type = USER_PROCESS;
184     if (!getutid(&utmp)) {
185         return;
186     }
187     utmp.ut_type = DEAD_PROCESS;
188     utmp.ut_time = time(NULL);
189     pututline(&utmp);
190     getutmpx(&utmp, &utmpx);
191     update_wtmp(WTMP_FILENAME, &utmpx);
192     endutent();
193 
194 #   else /* HAVE_UTMPX_H */
195     struct utmp *putmp;
196     pid_t pid = getpid();
197 
198     if (!ut_id[0])
199         return;                 /* entry not made */
200 
201     utmpname(UTMP_FILENAME);
202     setutent();
203     /*
204      * The following code waw copied from the poeigl-1.20 login/init package.
205      * Special thanks to poe for the code examples.
206      */
207     while ((putmp = getutent())) {
208         if (putmp->ut_pid == pid) {
209             putmp->ut_type = DEAD_PROCESS;
210             putmp->ut_pid = 0;
211             putmp->ut_user[0] = '\0';
212             putmp->ut_time = time(NULL);
213             pututline(putmp);
214             update_wtmp(WTMP_FILENAME, putmp);
215             break;
216         }
217     }
218     endutent();
219 #   endif /* HAVE_UTMPX_H */
220 }
221 
222 # else /* USE_SYSV_UTMP */
223 /* BSD utmp support */
224 
225 #  ifdef NEW_BSD_UTMP
226 
227 /* used to hold the line we are using */
228 static char ut_line[32];
229 
230 static int
get_tslot(const char * ttyname)231 get_tslot(const char *ttyname)
232 {
233     register struct ttyent *ttyp;
234     register int slot;
235 
236     setttyent();
237     for (slot = 1; (ttyp = getttyent()); ++slot)
238         if (!strcmp(ttyp->ty_name, ttyname)) {
239             endttyent();
240             return (slot);
241         }
242     endttyent();
243     return 0;
244 }
245 
246 void
b_login(struct utmp * ut)247 b_login(struct utmp *ut)
248 {
249     /*
250      ** replacement for freebsd's login(), which uses ttyslot()
251      **
252      ** like I shouldn't have just KNOWN that from the comment on get_tslot
253      ** below...
254      **            - brian
255      */
256     register int fd;
257     int tty;
258 
259     tty = get_tslot(ut->ut_line);
260     if (tty > 0 && (fd = open(_PATH_UTMP, O_WRONLY | O_CREAT, 0644)) >= 0) {
261         (void) lseek(fd, (off_t) (tty * sizeof(struct utmp)), L_SET);
262         (void) write(fd, ut, sizeof(struct utmp));
263 
264         (void) close(fd);
265     }
266     if ((fd = open(_PATH_WTMP, O_WRONLY | O_APPEND, 0)) >= 0) {
267         (void) write(fd, ut, sizeof(struct utmp));
268 
269         (void) close(fd);
270     }
271 }
272 
273 #  else /* NEW_BSD_UTMP */
274 static int utmp_pos = 0;        /* position of utmp-stamp */
275 
276 /*----------------------------------------------------------------------*
277  * get_tslot() - grabbed from xvt-1.0 - modified by David Perry
278  *
279  * find ttyname in /etc/ttytab and return a slot number that can be used to
280  * access the utmp file.  Can't use ttyslot() because the tty name is not
281  * that of fd 0.
282  *----------------------------------------------------------------------*/
283 static int
get_tslot(const char * ttyname)284 get_tslot(const char *ttyname)
285 {
286     char buf[256], name[256];
287     FILE *fd;
288 
289     if ((fd = fopen(UTMP_FILENAME, "r"))) {
290         int i;
291 
292         for (i = 1; fgets(buf, sizeof(buf), fd); i++) {
293             if (*buf == '#' || sscanf(buf, "%s", name) != 1)
294                 continue;
295             if (!strcmp(ttyname, name)) {
296                 fclose(fd);
297                 return i;
298             }
299         }
300         fclose(fd);
301     }
302     return -1;
303 }
304 
305 /*
306  * write utmp entry to UTMP_FILENAME
307  */
308 static int
write_utmp(struct utmp * putmp)309 write_utmp(struct utmp *putmp)
310 {
311     int rval = 0;
312     FILE *fd;
313 
314     fprintf(stderr, "Utmp file is %s\n", UTMP_FILENAME);
315     privileges(INVOKE);
316     if ((fd = fopen(UTMP_FILENAME, "r+"))) {
317         utmp_pos = get_tslot(putmp->ut_line) * sizeof(struct utmp);
318 
319         if (utmp_pos >= 0) {
320             fseek(fd, utmp_pos, 0);
321             fwrite(putmp, sizeof(struct utmp), 1, fd);
322 
323             rval = 1;
324         }
325         fclose(fd);
326     }
327     privileges(REVERT);
328     return rval;
329 }
330 
331 #  endif /* NEW_BSD_UTMP */
332 
333 void
add_utmp_entry(const char * pty,const char * hostname,int fd)334 add_utmp_entry(const char *pty, const char *hostname, int fd)
335 {
336     struct passwd *pwent = getpwuid(my_ruid);
337     struct utmp utmp;
338 
339     MEMSET(&utmp, 0, sizeof(struct utmp));
340 
341     if (!strncmp(pty, "/dev/", 5))
342         pty += 5;               /* skip /dev/ prefix */
343 
344 #  ifdef NEW_BSD_UTMP
345     strncpy(ut_line, pty, 31);
346 
347     strncpy(utmp.ut_line, pty, UT_LINESIZE);
348     strncpy(utmp.ut_name, pwent->pw_name, UT_NAMESIZE);
349     strncpy(utmp.ut_host, hostname, UT_HOSTSIZE);
350     utmp.ut_time = time(NULL);
351 
352     b_login(&utmp);
353 #  else /* NEW_BSD_UTMP */
354     if (!strncmp(pty, "pty", 3) || !strncmp(pty, "tty", 3))
355         strncpy(ut_id, (pty + 3), sizeof(ut_id));       /* bsd naming */
356     else {
357         libast_print_error("can't parse tty name \"%s\"\n", pty);
358         ut_id[0] = '\0';        /* entry not made */
359         return;
360     }
361 
362     strncpy(utmp.ut_line, ut_id, sizeof(utmp.ut_line));
363     strncpy(utmp.ut_name, pwent->pw_name, sizeof(utmp.ut_name));
364     strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
365     utmp.ut_time = time(NULL);
366 
367     if (write_utmp(&utmp) < 0)
368         ut_id[0] = '\0';        /* entry not made */
369 #  endif
370     return;
371     fd = 0;
372 }
373 
374 /*
375  * remove a utmp entry
376  */
377 void
remove_utmp_entry(void)378 remove_utmp_entry(void)
379 {
380 #  ifdef NEW_BSD_UTMP
381     logout(ut_line);
382     logwtmp(ut_line, "", "");
383 #  else /* NEW_BSD_UTMP */
384     FILE *fd;
385 
386     privileges(INVOKE);
387     if (!ut_id[0] && (fd = fopen(UTMP_FILENAME, "r+"))) {
388         struct utmp utmp;
389         MEMSET(&utmp, 0, sizeof(struct utmp));
390 
391         fseek(fd, utmp_pos, 0);
392         fwrite(&utmp, sizeof(struct utmp), 1, fd);
393 
394         fclose(fd);
395     }
396     privileges(REVERT);
397 #  endif /* NEW_BSD_UTMP */
398 }
399 
400 # endif /* USE_SYSV_UTMP */
401 
402 #endif /* UTMP_SUPPORT */
403