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