/* * trickled.c * * Copyright (c) 2003 Marius Aamodt Eriksen * All rights reserved. * * $Id: trickled.c,v 1.23 2003/05/09 02:16:42 marius Exp $ */ /* * NOTES * - on the first transmission; limit the #bytes to smoothing length to avoid * initial spike */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ #include #include #ifdef HAVE_ERR_H #include #endif /* HAVE_ERR_H */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #endif /* HAVE_STDINT_H */ #if defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME) #include #endif /* defined(HAVE_TIME_H) && defined(TIME_WITH_SYS_TIME) */ #include "trickle.h" #include "print.h" #include "util.h" #include "message.h" #include "client.h" #include "conf.h" #include "cleanup.h" void trickled_setup(char *); void newclientcb(int, short, void *); static void killclient(struct client *); void msgcb(int, short, void *); void notifycb(int, short, void *); void usage(void); void gensigcb(int, short, void *); void forcecb(int, short, void *); #define CONF_SAVE(w, f) do { \ char *p = f; \ if (p != NULL) \ (w) = p; \ } while (0) #ifdef HAVE___PROGNAME extern char *__progname; #else char *__progname; #endif struct event listenev; int winsz = 1024 * 200; double tsmooth = 5; uint lsmooth = 10, pri = 1, globlim[2] = { 10 * 1024, 10 * 1024 }; char *conf_path = SYSCONFDIR "/trickled.conf"; struct event sighupev, sigtermev, sigintev, notifyev, forceev; cleanup_t *cleanup; struct timeval notifytv, forcetv = {5, 0}; int main(int argc, char **argv) { int opt, verbose = 0, fg = 0, usesyslog = 0; struct stat sb; static char sockname[MAXPATHLEN]; __progname = get_progname(argv[0]); print_setup(verbose, usesyslog); sockname[0] = '\0'; #define GETOPTSTR "Vvfhsu:d:c:l:t:p:w:n:N:" while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) if (opt == 'c') conf_path = optarg; optind = 1; if (stat(conf_path, &sb) == -1 && errno == ENOENT) warnv(0, "Skipping configuration file: %s", conf_path); while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) switch (opt) { case 'v': verbose++; break; case 'u': globlim[TRICKLE_SEND] = 1024 * atoi(optarg); break; case 'd': globlim[TRICKLE_RECV] = 1024 * atoi(optarg); break; case 'f': fg = 1; break; case 'V': warnxv(0, PACKAGE " version " VERSION); exit(0); case 't': tsmooth = strtod(optarg, (char **)NULL); break; case 'l': lsmooth = atoi(optarg); break; case 's': usesyslog = 1; break; case 'p': pri = atoi(optarg); if (pri > 20 || pri < 0) { warnxv(0, "Invalid priority %d", pri); usage(); } break; case 'w': winsz = 1024 * atoi(optarg); break; case 'n': strlcpy(sockname, optarg, sizeof(sockname)); break; case 'N': notifytv.tv_sec = atoi(optarg); break; case 'c': break; case 'h': default: usage(); } argc -= optind; argv += optind; conf_init(); print_setup(verbose, usesyslog); if (!fg && daemon(1, 0) == -1) errv(0, 1, "daemon()"); event_init(); if (sockname[0] == '\0') strlcpy(sockname, "/tmp/.trickled.sock", sizeof(sockname)); if (stat(sockname, &sb) != -1) errxv(0, 1, "Socket name %s not free", sockname); if ((cleanup = cleanup_new()) == NULL) errxv(0, 1, "Failed setting up cleanup functionality"); client_init(winsz); trickled_setup(sockname); warnxv(1, "Using socket name: %s", sockname); signal_set(&sigtermev, SIGTERM, gensigcb, NULL); if (signal_add(&sigtermev, NULL) == -1) errv(0, 1, "signal_add()"); signal_set(&sigintev, SIGINT, gensigcb, NULL); if (signal_add(&sigintev, NULL) == -1) errv(0, 1, "signal_add()"); if (timerisset(¬ifytv)) { evtimer_set(¬ifyev, notifycb, NULL); evtimer_add(¬ifyev, ¬ifytv); } evtimer_set(&forceev, forcecb, NULL); evtimer_add(&forceev, &forcetv); event_dispatch(); err(1, "event error"); /* NOTREACHED */ return (1); } void trickled_setup(char *sockname) { int s; struct sockaddr_un xsun; if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "socket()"); memset(&xsun, 0, sizeof(xsun)); xsun.sun_family = AF_UNIX; strlcpy(xsun.sun_path, sockname, sizeof(xsun.sun_path)); if (bind(s, (struct sockaddr *)&xsun, sizeof(xsun)) == -1) errv(0, 1, "bind()"); if (listen(s, 10) == -1) errv(0, 1, "listen()"); /* We want to serve everybody */ if (chmod(sockname, S_IRWXU | S_IRWXG | S_IRWXO) == -1) errv(0, 1, "chmod()"); event_set(&listenev, s, EV_READ, newclientcb, NULL); if (event_add(&listenev, NULL) == -1) errv(0, 1, "event_add()"); /* Make sure we are good citizens */ cleanup_add(cleanup, cleanupcb_unlink, sockname); } void forcecb(int fd, short which, void *arg) { client_force(); if (evtimer_add(&forceev, &forcetv) == -1) err(1, "event_add()"); } void newclientcb(int fd, short which, void *arg) { struct sockaddr sa; int s; socklen_t salen = sizeof(sa); struct client *cli; if ((s = accept(fd, &sa, &salen)) == -1) { warn("accept()"); goto out; } if ((cli = calloc(1, sizeof(*cli))) == NULL) { warn("calloc()"); goto out; } cli->s = s; event_set(&cli->ev, s, EV_READ, msgcb, cli); if (event_add(&cli->ev, NULL) == -1) warn("event_add()"); out: if (event_add(&listenev, NULL) == -1) err(1, "event_add()"); } void msgcb(int fd, short which, void *arg) { struct client *cli = arg; struct msg msg; if (client_recvmsg(cli, &msg) == -1) { killclient(cli); return; } switch (msg.type) { case MSG_TYPE_SPECTATOR: SET(cli->flags, CLIENT_CONFIGURED | CLIENT_SPECTATOR); goto out; break; case MSG_TYPE_CONF: { struct passwd *pw; struct group *gr; struct msg_conf *conf = &msg.data.conf; cli->pid = conf->pid; cli->uid = conf->uid; cli->gid = conf->gid; memcpy(cli->lim, conf->lim, sizeof(cli->lim)); strlcpy(cli->argv0, conf->argv0, sizeof(cli->argv0)); /* XXX These kills seems kind of brutal */ if ((pw = getpwuid(cli->uid)) != NULL) { strlcpy(cli->uname, pw->pw_name, sizeof(cli->uname)); } else { warnxv(1, "Failed to translate UID to name"); killclient(cli); return; } if ((gr = getgrgid(cli->gid)) != NULL) { strlcpy(cli->gname, gr->gr_name, sizeof(cli->gname)); } else { warnxv(1, "Failed to translate GID to name"); killclient(cli); return; } cli->pri = conf_get_num(cli->argv0, "Priority", pri); cli->tsmooth = conf_get_fnum(cli->argv0, "Time-Smoothing", tsmooth); cli->lsmooth = conf_get_num(cli->argv0, "Length-Smoothing", lsmooth); if (client_register(cli) == -1) { warnxv(1, "Failed to register client"); killclient(cli); return; } if (client_configure(cli) == -1) { warnxv(1, "Client configuration error"); killclient(cli); return; } SET(cli->flags, CLIENT_CONFIGURED); warnxv(1, "New client: %d (%s/%s)", cli->pid, cli->argv0, cli->uname); goto out; break; } default: if (!ISSET(cli->flags, CLIENT_CONFIGURED)) goto out; } /* Spectators have a limited "command set." */ if (msg.type < MSG_TYPE_GETINFO && ISSET(cli->flags, CLIENT_SPECTATOR)) goto out; switch (msg.type) { case MSG_TYPE_UPDATE: { struct msg_update *update = &msg.data.update; client_update(cli, update->dir, update->len); break; } case MSG_TYPE_DELAY: { struct msg_delay *delay = &msg.data.delay; client_delay(cli, delay->dir, delay->len, globlim[delay->dir]); break; } case MSG_TYPE_GETDELAY: { struct msg_delay *delay = &msg.data.delay; client_getdelay(cli, delay->dir, delay->len, globlim[delay->dir]); break; } case MSG_TYPE_GETINFO: { client_getinfo(cli, globlim[TRICKLE_SEND], globlim[TRICKLE_RECV]); break; } default: warnxv(0, "Unknown message type %d", msg.type); break; } out: if (event_add(&cli->ev, NULL) == -1) warn("event_add()"); } static void killclient(struct client *cli) { close(cli->s); if (ISSET(cli->flags, CLIENT_CONFIGURED)) { if (!ISSET(cli->flags, CLIENT_SPECTATOR)) { client_unregister(cli); warnxv(1, "Removed client: %d (%s/%s)", cli->pid, cli->argv0, cli->uname); } else warnxv(1, "Removed spectator"); } free(cli); return; } void notifycb(int fd, short ev, void *data) { client_printrates(); evtimer_add(¬ifyev, ¬ifytv); } void gensigcb(int sig, short ev, void *data) { char *sigstr; switch (sig) { case SIGTERM: sigstr = "SIGTERM"; break; case SIGINT: sigstr = "SIGINT"; break; default: sigstr = "an unknown signal"; break; } cleanup_cleanup(cleanup); errxv(0, 0, "Received %s; quitting", sigstr); } void usage(void) { fprintf(stderr, "Usage: %s [-hvVfs] [-d ] [-u ] [-t ] " "[-l ]\n" " %*c [-p ] [-c ] [-n ] [-N ]\n" " %*c [-w ]\n" "\t-h Help (this)\n" "\t-v Increase verbosity level\n" "\t-V Print %s version\n" "\t-f Run %s in the foreground\n" "\t-s Use syslog instead of stderr to print messages\n" "\t-d Set maximum cumulative download rate to KB/s\n" "\t-u Set maximum cumulative upload rate to KB/s\n" "\t-t Set default smoothing time to s\n" "\t-l Set default smoothing length to KB\n" "\t-p Set default priority to \n" "\t-c Use configuration file \n" "\t-n Set socket name to \n" "\t-N Notify of bandwidth usage every s\n" "\t-w Set window size to s\n", __progname, (int)strlen(__progname), ' ', (int)strlen(__progname), ' ', __progname, __progname); exit(1); }