1 #include "config.h"
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <unistd.h>
7 #include <errno.h>
8 #include <signal.h>
9 #include <syslog.h>
10 #include <fcntl.h>
11 #include <pwd.h>
12 #include <grp.h>
13 #include <time.h>
14 #include <sys/ioctl.h>
15 #include <sys/time.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18 #include <sys/signal.h>
19 #include <sys/utsname.h>
20 #include <sys/socket.h>
21 #include <sys/un.h>
22 #include <netinet/in.h>
23 #include <arpa/inet.h>
24 #include <netdb.h>
25 #ifdef __linux__
26 # include "videodev.h"
27 #endif
28
29 #include "httpd.h"
30 #include "devices.h"
31 #include "vbi-data.h"
32
33 /* ---------------------------------------------------------------------- */
34 /* public variables - server configuration */
35
36 char *server_name = "alevtd/2.0";
37
38 int debug = 0;
39 int dontdetach = 0;
40 int timeout = 60;
41 int keepalive_time = 5;
42 int tcp_port = 0;
43 int ascii_art = 0;
44 char *listen_ip = NULL;
45 char *listen_port = "5654";
46 int canonicalhost = 0;
47 char server_host[256];
48 char user[17];
49 char group[17];
50 char *logfile = NULL;
51 FILE *logfd = NULL;
52 int flushlog = 0;
53 int usesyslog = 0;
54 int have_tty = 1;
55 int max_conn = 32;
56 int cachereset = 0;
57
58 time_t now,start;
59 int slisten;
60
61 struct vbi_state *vbi;
62
63 /* ---------------------------------------------------------------------- */
64
65 static int termsig,got_sighup,got_sigusr1;
66
67 static void catchsig(int sig)
68 {
69 if (SIGTERM == sig || SIGINT == sig)
70 termsig = sig;
71 if (SIGHUP == sig)
72 got_sighup = 1;
73 if (SIGUSR1 == sig)
74 got_sigusr1 = 1;
75 }
76
77 /* ---------------------------------------------------------------------- */
78
79 static void
80 usage(char *name)
81 {
82 char *h;
83 struct passwd *pw;
84 struct group *gr;
85
86 h = strrchr(name,'/');
87 fprintf(stderr,
88 "alevt http daemon.\n"
89 "\n"
90 "usage: %s [ options ]\n"
91 "\n"
92 "Options:\n"
93 " -h print this text\n"
94 " -v dev vbi device [%s]\n"
95 " -d enable debug output [%s]\n"
96 " -F do not fork into background [%s]\n"
97 " -s enable syslog (start/stop/errors) [%s]\n"
98 " -t sec set network timeout [%i]\n"
99 " -c n set max. allowed connections [%i]\n"
100 " -p port use tcp-port >port< [%s]\n"
101 " -n host server hostname is >host< [%s]\n"
102 " -N host same as above + UseCanonicalName\n"
103 " -i ip bind to IP-address >ip< [%s]\n"
104 " -l log write access log to file >log< [%s]\n"
105 " -L log same as above + flush every line\n"
106 #if 0
107 " -r poll tv frequency and clear cache\n"
108 " on station changes [%s]\n"
109 " -a use ascii art for block graphics [%s]\n"
110 #endif
111 "",
112 h ? h+1 : name,
113 ng_dev.vbi,
114 debug ? "on" : "off",
115 dontdetach ? "on" : "off",
116 usesyslog ? "on" : "off",
117 timeout, max_conn,
118 listen_port,
119 server_host,
120 listen_ip ? listen_ip : "any",
121 logfile ? logfile : "none");
122 #if 0
123 logfile ? logfile : "none",
124 cachereset ? "on" : "off",
125 ascii_art ? "on" : "off");
126 #endif
127 if (getuid() == 0) {
128 pw = getpwuid(0);
129 gr = getgrgid(getgid());
130 fprintf(stderr,
131 " -u user run as user >user< [%s]\n"
132 " -g group run as group >group< [%s]\n",
133 pw ? pw->pw_name : "???",
134 gr ? gr->gr_name : "???");
135 }
136 exit(1);
137 }
138
139 static void
140 fix_ug(void)
141 {
142 struct passwd *pw = NULL;
143 struct group *gr = NULL;
144
145 /* root is allowed to use any uid/gid,
146 * others will get their real uid/gid */
147 if (0 == getuid() && strlen(user) > 0) {
148 if (NULL == (pw = getpwnam(user)))
149 pw = getpwuid(atoi(user));
150 } else {
151 pw = getpwuid(getuid());
152 }
153 if (0 == getuid() && strlen(group) > 0) {
154 if (NULL == (gr = getgrnam(group)))
155 gr = getgrgid(atoi(group));
156 } else {
157 gr = getgrgid(getgid());
158 }
159
160 if (NULL == pw) {
161 xerror(LOG_ERR,"user unknown",NULL);
162 exit(1);
163 }
164 if (NULL == gr) {
165 xerror(LOG_ERR,"group unknown",NULL);
166 exit(1);
167 }
168
169 /* set group */
170 if (getegid() != gr->gr_gid || getgid() != gr->gr_gid)
171 setgid(gr->gr_gid);
172 if (getegid() != gr->gr_gid || getgid() != gr->gr_gid) {
173 xerror(LOG_ERR,"setgid failed",NULL);
174 exit(1);
175 }
176 strncpy(group,gr->gr_name,16);
177
178 /* set user */
179 if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid)
180 setuid(pw->pw_uid);
181 if (geteuid() != pw->pw_uid || getuid() != pw->pw_uid) {
182 xerror(LOG_ERR,"setuid failed",NULL);
183 exit(1);
184 }
185 strncpy(user,pw->pw_name,16);
186 }
187
188 /* ---------------------------------------------------------------------- */
189
190 static void
191 access_log(struct REQUEST *req, time_t now)
192 {
193 char timestamp[32];
194
195 /* common log format: host ident authuser date request status bytes */
196 strftime(timestamp,31,"[%d/%b/%Y:%H:%M:%S +0000]",gmtime(&now));
197 if (0 == req->status)
198 req->status = 400; /* bad request */
199 if (400 == req->status) {
200 fprintf(logfd,"%s - - %s \"-\" 400 %d\n",
201 req->peerhost,
202 timestamp,
203 req->bc);
204 } else {
205 fprintf(logfd,"%s - - %s \"%s %s HTTP/%d.%d\" %d %d\n",
206 req->peerhost,
207 timestamp,
208 req->type,
209 req->uri,
210 req->major,
211 req->minor,
212 req->status,
213 req->bc);
214 }
215 if (flushlog)
216 fflush(logfd);
217 }
218
219 /*
220 * loglevel usage
221 * ERR : fatal errors (which are followed by exit(1))
222 * WARNING: this should'nt happen error (oom, ...)
223 * NOTICE : start/stop of the daemon
224 * INFO : "normal" errors (canceled downloads, timeouts,
225 * stuff what happens all the time)
226 */
227
228 static void
229 syslog_init(void)
230 {
231 openlog("alevtd",LOG_PID, LOG_DAEMON);
232 }
233
234 static void
235 syslog_start(void)
236 {
237 syslog(LOG_NOTICE,
238 "started (listen on %s:%d, user=%s, group=%s)\n",
239 listen_ip,tcp_port,user,group);
240 }
241
242 static void
243 syslog_stop(void)
244 {
245 if (termsig)
246 syslog(LOG_NOTICE,"stopped on signal %d\n",termsig);
247 else
248 syslog(LOG_NOTICE,"stopped\n");
249 closelog();
250 }
251
252 void
253 xperror(int loglevel, char *txt, char *peerhost)
254 {
255 if (LOG_INFO == loglevel && usesyslog < 2 && !debug)
256 return;
257 if (have_tty) {
258 if (NULL == peerhost)
259 perror(txt);
260 else
261 fprintf(stderr,"%s: %s (peer=%s)\n",txt,strerror(errno),
262 peerhost);
263 }
264 if (usesyslog) {
265 if (NULL == peerhost)
266 syslog(loglevel,"%s: %s\n",txt,strerror(errno));
267 else
268 syslog(loglevel,"%s: %s (peer=%s)\n",txt,strerror(errno),
269 peerhost);
270 }
271 }
272
273 void
274 xerror(int loglevel, char *txt, char *peerhost)
275 {
276 if (LOG_INFO == loglevel && usesyslog < 2 && !debug)
277 return;
278 if (have_tty) {
279 if (NULL == peerhost)
280 fprintf(stderr,"%s\n",txt);
281 else
282 fprintf(stderr,"%s (peer=%s)\n",txt,peerhost);
283 }
284 if (usesyslog) {
285 if (NULL == peerhost)
286 syslog(loglevel,"%s\n",txt);
287 else
288 syslog(loglevel,"%s (peer=%s)\n",txt,peerhost);
289 }
290 }
291
292 static void
293 dummy_handler(vbi_event *ev, void *unused)
294 {
295 return;
296 }
297
298 /* ---------------------------------------------------------------------- */
299 /* main loop */
300
301 static void*
302 mainloop(void)
303 {
304 struct REQUEST *conns = NULL;
305 int curr_conn = 0;
306
307 struct REQUEST *req,*prev,*tmp;
308 struct timeval tv;
309 int max,length;
310 fd_set rd,wr;
311
312 for (;!termsig;) {
313 if (got_sighup) {
314 if (NULL != logfile && 0 != strcmp(logfile,"-")) {
315 if (debug)
316 fprintf(stderr,"got SIGHUP, reopen logfile %s\n",logfile);
317 if (logfd)
318 fclose(logfd);
319 if (NULL == (logfd = fopen(logfile,"a")))
320 xperror(LOG_WARNING,"reopen access log",NULL);
321 }
322 got_sighup = 0;
323 }
324 #if 0
325 if (got_sigusr1) {
326 if (debug)
327 fprintf(stderr,"got SIGUSR1, reset cached vbi pages\n");
328 vbi->cache->op->reset(vbi->cache);
329 got_sigusr1 = 0;
330 }
331 #endif
332
333 FD_ZERO(&rd);
334 FD_ZERO(&wr);
335 FD_SET(vbi->fd,&rd);
336 max = vbi->fd;
337 /* add listening socket */
338 if (curr_conn < max_conn) {
339 FD_SET(slisten,&rd);
340 max = slisten;
341 }
342 /* add connection sockets */
343 for (req = conns; req != NULL; req = req->next) {
344 switch (req->state) {
345 case STATE_KEEPALIVE:
346 case STATE_READ_HEADER:
347 FD_SET(req->fd,&rd);
348 if (req->fd > max)
349 max = req->fd;
350 break;
351 case STATE_WRITE_HEADER:
352 case STATE_WRITE_BODY:
353 FD_SET(req->fd,&wr);
354 if (req->fd > max)
355 max = req->fd;
356 break;
357 }
358 }
359 /* go! */
360 tv.tv_sec = keepalive_time;
361 tv.tv_usec = 0;
362 if (-1 == select(max+1,&rd,&wr,NULL,(curr_conn > 0) ? &tv : NULL)) {
363 if (debug)
364 perror("select");
365 continue;
366 }
367 now = time(NULL);
368
369 /* vbi data? */
370 if (FD_ISSET(vbi->fd,&rd)) {
371 #if 0 /* def __linux__ */
372 if (cachereset) {
373 ioctl(vbi->fd, VIDIOCGFREQ, &freq);
374 if (lastfreq != freq) {
375 lastfreq = freq;
376 vbi->cache->op->reset( vbi->cache) ;
377 if (debug)
378 fprintf(stderr, "frequency change: cache cleared.\n");
379 }
380 }
381 #endif
382 vbi_hasdata(vbi);
383 }
384
385 /* new connection ? */
386 if (FD_ISSET(slisten,&rd)) {
387 req = malloc(sizeof(struct REQUEST));
388 if (NULL == req) {
389 /* oom: let the request sit in the listen queue */
390 if (debug)
391 fprintf(stderr,"oom\n");
392 } else {
393 memset(req,0,sizeof(struct REQUEST));
394 if (-1 == (req->fd = accept(slisten,NULL,NULL))) {
395 if (EAGAIN != errno)
396 xperror(LOG_WARNING,"accept",NULL);
397 free(req);
398 } else {
399 fcntl(req->fd,F_SETFL,O_NONBLOCK);
400 req->state = STATE_READ_HEADER;
401 req->ping = now;
402 req->next = conns;
403 conns = req;
404 curr_conn++;
405 if (debug)
406 fprintf(stderr,"%03d: new request (%d)\n",req->fd,curr_conn);
407 length = sizeof(req->peer);
408 if (-1 == getpeername(req->fd,(struct sockaddr*)&(req->peer),&length)) {
409 xperror(LOG_WARNING,"getpeername",NULL);
410 req->state = STATE_CLOSE;
411 }
412 getnameinfo((struct sockaddr*)&req->peer,length,
413 req->peerhost,64,req->peerserv,8,
414 NI_NUMERICHOST | NI_NUMERICSERV);
415 if (debug)
416 fprintf(stderr,"%03d: connect from (%s)\n",
417 req->fd,req->peerhost);
418 }
419 }
420 }
421
422 /* check active connections */
423 for (req = conns, prev = NULL; req != NULL;) {
424 /* I/O */
425 if (FD_ISSET(req->fd,&rd)) {
426 if (req->state == STATE_KEEPALIVE)
427 req->state = STATE_READ_HEADER;
428 read_request(req,0);
429 req->ping = now;
430 }
431 if (FD_ISSET(req->fd,&wr)) {
432 write_request(req);
433 req->ping = now;
434 }
435
436 /* check timeouts */
437 if (req->state == STATE_KEEPALIVE) {
438 if (now > req->ping + keepalive_time) {
439 if (debug)
440 fprintf(stderr,"%03d: keepalive timeout\n",req->fd);
441 req->state = STATE_CLOSE;
442 }
443 } else {
444 if (now > req->ping + timeout) {
445 if (req->state == STATE_READ_HEADER) {
446 mkerror(req,408,0);
447 } else {
448 xerror(LOG_INFO,"network timeout",req->peerhost);
449 req->state = STATE_CLOSE;
450 }
451 }
452 }
453
454 /* header parsing */
455 header_parsing:
456 if (req->state == STATE_PARSE_HEADER) {
457 parse_request(req);
458 if (req->state == STATE_WRITE_HEADER)
459 write_request(req);
460 }
461
462 /* handle finished requests */
463 if (req->state == STATE_FINISHED && !req->keep_alive)
464 req->state = STATE_CLOSE;
465 if (req->state == STATE_FINISHED) {
466 if (logfd)
467 access_log(req,now);
468 /* cleanup */
469 if (req->free_the_mallocs)
470 free(req->body);
471 req->free_the_mallocs = 0;
472 req->body = NULL;
473 req->written = 0;
474 req->head_only = 0;
475
476 if (req->hdata == req->lreq) {
477 /* ok, wait for the next one ... */
478 if (debug)
479 fprintf(stderr,"%03d: keepalive wait\n",req->fd);
480 req->state = STATE_KEEPALIVE;
481 req->hdata = 0;
482 req->lreq = 0;
483 } else {
484 /* there is a pipelined request in the queue ... */
485 if (debug)
486 fprintf(stderr,"%03d: keepalive pipeline\n",req->fd);
487 req->state = STATE_READ_HEADER;
488 memmove(req->hreq,req->hreq+req->lreq,
489 req->hdata-req->lreq);
490 req->hdata -= req->lreq;
491 req->lreq = 0;
492 read_request(req,1);
493 goto header_parsing;
494 }
495 }
496
497 /* connections to close */
498 if (req->state == STATE_CLOSE) {
499 if (logfd)
500 access_log(req,now);
501 /* cleanup */
502 close(req->fd);
503 curr_conn--;
504 if (debug)
505 fprintf(stderr,"%03d: done (%d)\n",req->fd,curr_conn);
506 /* unlink from list */
507 tmp = req;
508 if (prev == NULL) {
509 conns = req->next;
510 req = conns;
511 } else {
512 prev->next = req->next;
513 req = req->next;
514 }
515 /* free memory */
516 if (tmp->free_the_mallocs)
517 free(tmp->body);
518 free(tmp);
519 } else {
520 prev = req;
521 req = req->next;
522 }
523 }
524 }
525 return NULL;
526 }
527
528 /* ---------------------------------------------------------------------- */
529
530 int
531 main(int argc, char *argv[])
532 {
533 struct sigaction act,old;
534 struct addrinfo ask,*res;
535 #ifdef HAVE_SOCKADDR_STORAGE
536 struct sockaddr_storage ss;
537 #else
538 struct sockaddr ss;
539 #endif
540 int c, opt, rc, ss_len, v4, v6, simulate;
541 char host[INET6_ADDRSTRLEN+1];
542 char serv[16];
543
544 ng_device_init();
545 gethostname(server_host,255);
546 memset(&ask,0,sizeof(ask));
547 ask.ai_flags = AI_CANONNAME;
548 if (0 == (rc = getaddrinfo(server_host, NULL, &ask, &res))) {
549 if (res->ai_canonname)
550 strcpy(server_host,res->ai_canonname);
551 }
552
553 /* parse options */
554 v4 = 1; v6 = 1; simulate = 0;
555 for (;;) {
556 if (-1 == (c = getopt(argc,argv,"hasdFr46S"
557 "p:N:n:i:t:c:u:g:l:L:v:")))
558 break;
559 switch (c) {
560 case 'h':
561 usage(argv[0]);
562 break;
563 case '4':
564 v4 = 1;
565 v6 = 0;
566 break;
567 case '6':
568 v4 = 0;
569 v6 = 1;
570 break;
571 case 'a':
572 ascii_art++;
573 break;
574 case 's':
575 usesyslog++;
576 break;
577 case 'S':
578 simulate++;
579 break;
580 case 'd':
581 debug++;
582 break;
583 case 'F':
584 dontdetach++;
585 break;
586 case 'r':
587 cachereset++;
588 break;
589 case 'N':
590 canonicalhost = 1;
591 /* fall through */
592 case 'n':
593 strncpy(server_host,optarg,sizeof(server_host)-1);
594 break;
595 case 'i':
596 listen_ip = optarg;
597 break;
598 case 'p':
599 listen_port = optarg;
600 break;
601 case 't':
602 timeout = atoi(optarg);
603 break;
604 case 'c':
605 max_conn = atoi(optarg);
606 break;
607 case 'u':
608 strncpy(user,optarg,16);
609 break;
610 case 'g':
611 strncpy(group,optarg,16);
612 break;
613 case 'L':
614 flushlog = 1;
615 /* fall through */
616 case 'l':
617 logfile = optarg;
618 break;
619 case 'v':
620 ng_dev.vbi = optarg;
621 break;
622 default:
623 exit(1);
624 }
625 }
626 if (usesyslog)
627 syslog_init();
628
629 /* open vbi device */
630 vbi = vbi_open(ng_dev.vbi,debug,simulate);
631 if (NULL == vbi) {
632 xperror(LOG_ERR,"cannot open vbi device",NULL);
633 exit(1);
634 }
635 if (debug)
636 vbi_event_handler_add(vbi->dec,~0,vbi_dump_event,vbi);
637 else
638 vbi_event_handler_add(vbi->dec,~0,dummy_handler,vbi);
639
640 /* bind to socket */
641 slisten = -1;
642 memset(&ask,0,sizeof(ask));
643 ask.ai_flags = AI_PASSIVE;
644 if (listen_ip)
645 ask.ai_flags |= AI_CANONNAME;
646 ask.ai_socktype = SOCK_STREAM;
647
648 /* try ipv6 first ... */
649 if (-1 == slisten && v6) {
650 ask.ai_family = PF_INET6;
651 if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) {
652 if (debug)
653 fprintf(stderr,"getaddrinfo (ipv6): %s\n",gai_strerror(rc));
654 } else {
655 if (-1 == (slisten = socket(res->ai_family, res->ai_socktype,
656 res->ai_protocol)) && debug)
657 xperror(LOG_ERR,"socket (ipv6)",NULL);
658 }
659 }
660
661 /* ... failing that try ipv4 */
662 if (-1 == slisten && v4) {
663 ask.ai_family = PF_INET;
664 if (0 != (rc = getaddrinfo(listen_ip, listen_port, &ask, &res))) {
665 fprintf(stderr,"getaddrinfo (ipv4): %s\n",gai_strerror(rc));
666 } else {
667 if (-1 == (slisten = socket(res->ai_family, res->ai_socktype,
668 res->ai_protocol)))
669 xperror(LOG_ERR,"socket (ipv4)",NULL);
670 }
671 }
672
673 if (-1 == slisten)
674 exit(1);
675
676 memcpy(&ss,res->ai_addr,res->ai_addrlen);
677 ss_len = res->ai_addrlen;
678 if (res->ai_canonname)
679 strcpy(server_host,res->ai_canonname);
680 if (0 != (rc = getnameinfo((struct sockaddr*)&ss,ss_len,
681 host,INET6_ADDRSTRLEN,serv,15,
682 NI_NUMERICHOST | NI_NUMERICSERV))) {
683 fprintf(stderr,"getnameinfo: %s\n",gai_strerror(rc));
684 exit(1);
685 }
686
687 tcp_port = atoi(serv);
688 opt = 1;
689 setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
690 fcntl(slisten,F_SETFL,O_NONBLOCK);
691
692 if (-1 == bind(slisten, (struct sockaddr*) &ss, ss_len)) {
693 xperror(LOG_ERR,"bind",NULL);
694 exit(1);
695 }
696 if (-1 == listen(slisten, 2*max_conn)) {
697 xperror(LOG_ERR,"listen",NULL);
698 exit(1);
699 }
700
701 /* change user/group - also does chroot */
702 fix_ug();
703
704 if (logfile) {
705 if (0 == strcmp(logfile,"-")) {
706 logfd = stdout;
707 } else {
708 if (NULL == (logfd = fopen(logfile,"a")))
709 xperror(LOG_WARNING,"open access log",NULL);
710 }
711 }
712
713 if (debug) {
714 fprintf(stderr,
715 "alevt http server started\n"
716 " ipv6 : %s\n"
717 " node : %s\n"
718 " ipaddr: %s\n"
719 " port : %d\n"
720 " user : %s\n"
721 " group : %s\n",
722 res->ai_family == PF_INET6 ? "yes" : "no",
723 server_host,host,tcp_port,user,group);
724 }
725
726 /* run as daemon - detach from terminal */
727 if ((!debug) && (!dontdetach)) {
728 switch (fork()) {
729 case -1:
730 xperror(LOG_ERR,"fork",NULL);
731 exit(1);
732 case 0:
733 close(0); close(1); close(2); setsid();
734 have_tty = 0;
735 break;
736 default:
737 exit(0);
738 }
739 }
740 if (usesyslog) {
741 syslog_start();
742 atexit(syslog_stop);
743 }
744
745 /* setup signal handler */
746 memset(&act,0,sizeof(act));
747 sigemptyset(&act.sa_mask);
748 act.sa_handler = SIG_IGN;
749 sigaction(SIGPIPE,&act,&old);
750 act.sa_handler = catchsig;
751 sigaction(SIGHUP,&act,&old);
752 sigaction(SIGUSR1,&act,&old);
753 sigaction(SIGTERM,&act,&old);
754 if (debug)
755 sigaction(SIGINT,&act,&old);
756
757 /* go! */
758 start = time(NULL);
759 mainloop();
760
761 if (logfd)
762 fclose(logfd);
763 if (debug)
764 fprintf(stderr,"bye...\n");
765 exit(0);
766 }
767
|
This page was automatically generated by the
LXR engine.
|