]> mj.ucw.cz Git - nwho.git/blob - nwhod.c
Also showing number of logged-in users in nuptime.
[nwho.git] / nwhod.c
1 /*
2  *      The Remote User Information Daemon 1.8
3  *
4  *      (c) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
5  *
6  *      This software may be freely distributed and used according to the terms
7  *      of the GNU General Public License. See file COPYING in any of the GNU packages.
8  */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/socket.h>
17 #include <netdb.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <syslog.h>
21 #include <sys/stat.h>
22 #include <utmp.h>
23 #include <signal.h>
24 #include <dirent.h>
25
26 #include "net.h"
27
28 struct hostrec {
29   struct hostrec *next;
30   char name[32];
31   time_t last_rec;                      /* 0=down */
32   __u32 addr;
33 };
34
35 static int sock, port;
36 static struct hostrec *first_host;
37 static time_t now, last_local_scan;
38
39 static void die(char *msg, ...) __attribute__((noreturn));
40
41 static void
42 die(char *msg, ...)
43 {
44   va_list args;
45
46   va_start(args, msg);
47   fprintf(stderr, "nwhod: ");
48   vfprintf(stderr, msg, args);
49   fputc('\n', stderr);
50   exit(1);
51 }
52
53 static void
54 net_init(char *name)
55 {
56   struct hostent *h;
57   struct sockaddr_in sa;
58   struct servent *se;
59   int one;
60
61   if (name)
62     {
63       if (! (h = gethostbyname(name)))
64         die("%s: %m", name);
65     }
66   else
67     h = NULL;
68
69   if (! (se = getservbyname("nwho", "udp")))
70     die("Unknown service `nwho/udp'");
71
72   sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
73   if (sock < 0)
74     die("socket: %m");
75
76   if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
77     die("setsockopt: %m");
78
79   sa.sin_family = AF_INET;
80   sa.sin_port = port = se->s_port;
81   sa.sin_addr.s_addr = INADDR_ANY;
82   if (bind(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
83     die("bind: %m");
84
85   if (h)
86     {
87       memcpy(&sa.sin_addr.s_addr, h->h_addr, sizeof(sa.sin_addr.s_addr));
88       if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
89         die("connect: %m");
90     }
91 }
92
93 static void
94 scan_utmp(struct rywho_pkt *p, time_t now)
95 {
96   struct utmp *u;
97   int cnt = 0;
98   struct stat st;
99   char device[32];
100   struct userinfo *h;
101   char name[UT_NAMESIZE+10];
102   time_t last;
103
104   setutent();
105   while ((u = getutent()) && cnt < MAX_USERS)
106     if (u->ut_type == USER_PROCESS && u->ut_user[0])
107       {
108         h = &p->users[cnt];
109         memcpy(name, u->ut_user, UT_NAMESIZE);
110         name[9] = 0;
111         strcpy(h->name, name);
112         h->login_time = htonl(now - u->ut_time);
113         if (u->ut_id[0])
114           {
115             h->con[0] = u->ut_id[0];
116             h->con[1] = u->ut_id[1];
117             h->con[2] = 0;
118           }
119         else
120           {
121             char *z = u->ut_line;
122             if (!strncmp(z, "tty", 3))
123               z += 3;
124             sprintf(h->con, "%.3s", z);
125           }
126         sprintf(device, "/dev/%s", u->ut_line);
127         if (stat(device, &st) < 0)
128           {
129             fprintf(stderr, "stat(%s): %m", device);
130             continue;
131           }
132         h->mesg_y = !!(S_IWGRP & st.st_mode);
133         last = st.st_atime;
134         if (st.st_mtime > last)
135           last = st.st_mtime;
136         if (st.st_ctime > last)
137           last = st.st_ctime;
138         if (now < last)
139           last = now;
140         h->idle_time = htonl(now - last);
141         cnt++;
142       }
143   p->num_users = htonl(cnt);
144 }
145
146 static void
147 scan_load(struct rywho_pkt *p)
148 {
149   int n, i, j[6];
150   char buf[256];
151
152   n = open("/proc/uptime", O_RDONLY);
153   if (n < 0)
154     return;
155   if (read(n, buf, sizeof(buf)) <= 0)
156     return;
157   close(n);
158   if (!sscanf(buf, "%d", &i))
159     return;
160   p->uptime = htonl(i);
161
162   n = open("/proc/loadavg", O_RDONLY);
163   if (n < 0)
164     return;
165   if (read(n, buf, sizeof(buf)) <= 0)
166     return;
167   close(n);
168   if (sscanf(buf, "%d.%d%d.%d%d.%d", &j[0], &j[1], &j[2], &j[3], &j[4], &j[5]) != 6)
169     return;
170   p->avl[0] = htonl(j[0]*100 + j[1]);
171   p->avl[1] = htonl(j[2]*100 + j[3]);
172   p->avl[2] = htonl(j[4]*100 + j[5]);
173 }
174
175 static void
176 make_pkt(struct rywho_pkt *pkt)
177 {
178   bzero(pkt, sizeof(pkt));
179   pkt->local_time = htonl(now);
180   scan_utmp(pkt, now);
181   scan_load(pkt);
182 }
183
184 static void
185 cleanup(void)
186 {
187   DIR *d;
188   struct dirent *e;
189
190   d = opendir(".");
191   if (!d)
192     die("opendir: %m");
193   while (e = readdir(d))
194     if (e->d_name[0] != '.')
195       unlink(e->d_name);
196   closedir(d);
197 }
198
199 static void
200 save_pkt(char *name, struct rywho_pkt *pkt, int len)
201 {
202   int fd = open(name, O_WRONLY | O_CREAT, 0666);
203   if (fd < 0)
204     {
205       syslog(LOG_ERR, "open(%s): %m", name);
206       return;
207     }
208   pkt->server_time = htonl(now);
209   if (write(fd, pkt, len) != len)
210     syslog(LOG_ERR, "write: %m");
211   ftruncate(fd, len);
212   close(fd);
213 }
214
215 static inline int                       /* Validation checks not implemented yet */
216 is_valid(__u32 ipa)
217 {
218   return 1;
219 }
220
221 static void
222 receive(void)
223 {
224   int r, al;
225   struct rywho_pkt pkt;
226   struct sockaddr_in sa;
227   int n = sizeof(struct rywho_pkt) - MAX_USERS * sizeof(struct userinfo);
228   struct hostrec *e;
229   struct hostent *h;
230   char *c;
231
232   alarm(DEFAULT_PRUNE_TIME);
233   al = sizeof(sa);
234   r = recvfrom(sock, &pkt, sizeof(pkt), 0, (struct sockaddr *) &sa, &al);
235   alarm(0);
236   now = time(NULL);
237   if (r < 0)
238     {
239       if (errno == EINTR)
240         return;
241       syslog(LOG_ERR, "recvfrom: %m");
242       sleep(5);
243       return;
244     }
245
246   if (!is_valid(sa.sin_addr.s_addr) || sa.sin_port != port)
247     {
248       syslog(LOG_WARNING, "Received packet from invalid source %s.%d", inet_ntoa(sa.sin_addr), ntohs(sa.sin_port));
249       return;
250     }
251
252   if (r < n || r != n + ntohl(pkt.num_users)*sizeof(struct userinfo))
253     {
254       syslog(LOG_WARNING, "Malformed packet from %s", inet_ntoa(sa.sin_addr));
255       return;
256     }
257
258   for(e=first_host; e; e=e->next)
259     if (e->addr == sa.sin_addr.s_addr)
260       break;
261   if (!e)
262     {
263       e = malloc(sizeof(struct hostrec));
264       if (!e)
265         {
266           syslog(LOG_ERR, "Out of memory");
267           return;
268         }
269       e->next = first_host;
270       first_host = e;
271       h = gethostbyaddr((char *) &sa.sin_addr, sizeof(sa.sin_addr), AF_INET);
272       if (h)
273         {
274           sprintf(e->name, "%.30s", h->h_name);
275           for(c=e->name; *c; c++)
276             if (*c == '.')
277               {
278                 *c = 0;
279                 break;
280               }
281             else if ((*c < 'A' || *c > 'Z') /* Filter out malicious characters */
282                      && (*c < 'a' || *c > 'z')
283                      && (*c < '0' || *c > '9')
284                      && *c != '-')
285               *c = '_';
286         }
287       else
288         strcpy(e->name, inet_ntoa(sa.sin_addr));
289       e->addr = sa.sin_addr.s_addr;
290     }
291
292   e->last_rec = now;
293   save_pkt(e->name, &pkt, r);
294 }
295
296 static void
297 local_scan(void)
298 {
299   struct rywho_pkt pkt;
300   static char hostname[64];
301
302   make_pkt(&pkt);
303   if (!hostname[0] && gethostname(hostname, sizeof(hostname)) < 0)
304     die("gethostname: %m");
305   save_pkt(hostname, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo));
306 }
307
308 static void
309 do_tick(void)
310 {
311   struct hostrec *e;
312
313   if (last_local_scan + DEFAULT_SEND_TIME <= now)
314     {
315       last_local_scan = now;
316       local_scan();
317     }
318   for(e=first_host; e; e=e->next)
319     if (e->last_rec && e->last_rec + DEFAULT_DEAD_TIME < now)
320       {
321         if (unlink(e->name) < 0)
322           syslog(LOG_ERR, "unlink(%s): %m", e->name);
323         e->last_rec = 0;
324       }
325 }
326
327 static void
328 tick(int unused)
329 {
330 }
331
332 static struct sigaction sigact = { tick, 0, 0, NULL };
333
334 static void
335 server(void)
336 {
337   net_init(NULL);
338   if (chdir(YWHO_SPOOL_DIR) < 0)
339     die("chdir(" YWHO_SPOOL_DIR "): %m");
340   cleanup();
341   sigaction(SIGALRM, &sigact, NULL);
342   now = time(NULL);
343   for(;;)
344     {
345       do_tick();
346       receive();
347     }
348 }
349
350 static void
351 client(char *serv)
352 {
353   struct rywho_pkt pkt;
354
355   net_init(serv);
356   utmpname(UTMP_FILE);
357   for(;;)
358     {
359       now = time(NULL);
360       make_pkt(&pkt);
361       if (send(sock, &pkt, sizeof(pkt) - (MAX_USERS - ntohl(pkt.num_users))*sizeof(struct userinfo), 0) < 0)
362         syslog(LOG_ERR, "sendmsg: %m");
363       sleep(DEFAULT_SEND_TIME);
364     }
365 }
366
367 int
368 main(int argc, char **argv)
369 {
370   int pid;
371
372   if (argc != 1 && argc != 2)
373     {
374       fprintf(stderr, "Usage: nwhod [<server>]\n");
375       return 1;
376     }
377   pid = fork();
378   if (pid < 0)
379     {
380       perror("fork");
381       return 1;
382     }
383   if (pid)
384     return 0;
385   if (argc == 2)
386     client(argv[1]);
387   else if (argc == 1)
388     server();
389   return 1;
390 }