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