+ struct tracker *t = c->tracker;
+ if (!t)
+ {
+ DLOG("put_tracker: no tracker?");
+ sleep(5);
+ return;
+ }
+ if (t->active_conns <= 0)
+ {
+ DLOG("put_tracker: no counter?");
+ sleep(5);
+ return;
+ }
+ t->active_conns--;
+ DTRACE("TBF: Put tracker (%d conns remain)", t->active_conns);
+}
+
+static void run_as_daemon(int do_fork)
+{
+ int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sk < 0)
+ die("socket: %m");
+
+ int one = 1;
+ if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+ die("setsockopt: %m");
+
+ struct sockaddr_in sa = {
+ .sin_family = AF_INET,
+ .sin_port = ntohs(1969),
+ .sin_addr.s_addr = INADDR_ANY,
+ };
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ die("bind: %m");
+ if (listen(sk, 128) < 0)
+ die("listen: %m");
+ // if (fcntl(sk, F_SETFL, O_NONBLOCK) < 0)
+ // die("fcntl: %m");
+
+ if (do_fork)
+ {
+ pid_t pid = fork();
+ if (pid < 0)
+ die("fork: %m");
+ if (pid)
+ {
+ FILE *f = fopen(PID_FILE, "w");
+ if (f)
+ {
+ fprintf(f, "%d\n", pid);
+ fclose(f);
+ }
+ exit(0);
+ }
+
+ chdir("/");
+ setresgid(GID, GID, GID);
+ setresuid(UID, UID, UID);
+ setsid();
+ }
+
+ struct sigaction sact = {
+ .sa_handler = sigchld_handler,
+ .sa_flags = SA_RESTART,
+ };
+ if (sigaction(SIGCHLD, &sact, NULL) < 0)
+ die("sigaction: %m");
+
+ DLOG("Daemon ready");
+ setproctitle("minsk: Listening");
+ openlog("minsk", LOG_PID, LOG_LOCAL7);
+
+ for (;;)
+ {
+ struct pollfd pfd[1] = {
+ { .fd = sk, .events = POLLIN },
+ };
+
+ int nfds = poll(pfd, 1, 60000);
+ if (nfds < 0 && errno != EINTR)
+ {
+ DLOG("poll: %m");
+ sleep(5);
+ continue;
+ }
+
+ int status;
+ pid_t pid;
+ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
+ {
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ DLOG("Process %d exited with strange status %x", pid, status);
+
+ struct conn *conn = pid_to_conn(pid);
+ if (conn)
+ {
+ DTRACE("Connection with PID %d exited", pid);
+ put_tracker(conn);
+ put_conn(conn);
+ }
+ else
+ DTRACE("PID %d exited, matching no connection", pid);
+ }
+
+ if (!(pfd[0].revents & POLLIN))
+ continue;
+
+ socklen_t salen = sizeof(sa);
+ int sk2 = accept(sk, (struct sockaddr *) &sa, &salen);
+ if (sk2 < 0)
+ {
+ if (errno != EINTR)
+ {
+ DLOG("accept: %m");
+ sleep(5);
+ }
+ continue;
+ }
+ DTRACE("Got connection: fd=%d", sk2);
+
+ struct conn *conn = get_conn(&sa.sin_addr);
+ const char *reason = NULL;
+ if (conn)
+ {
+ if (!get_tracker(conn))
+ {
+ DLOG("Connection from %s dropped: Throttling", inet_ntoa(sa.sin_addr));
+ put_conn(conn);
+ conn = NULL;
+ reason = "--- Sorry, but you are sending too many requests. Please slow down. ---\n";
+ }
+ }
+ else
+ {
+ DLOG("Connection from %s dropped: Too many connections", inet_ntoa(sa.sin_addr));
+ reason = "--- Sorry, maximum number of connections exceeded. Please come later. ---\n";
+ }
+
+ pid = fork();
+ if (pid < 0)
+ {
+ DLOG("fork failed: %m");
+ close(sk2);
+ continue;
+ }
+ if (!pid)
+ {
+ close(sk);
+ if (conn)
+ {
+ DLOG("Accepted connection from %s", inet_ntoa(sa.sin_addr));
+ setproctitle("minsk: %s", inet_ntoa(sa.sin_addr));
+ child(sk2);
+ }
+ else
+ {
+ DLOG("Sending error message to %s", inet_ntoa(sa.sin_addr));
+ setproctitle("minsk: %s ERR", inet_ntoa(sa.sin_addr));
+ write(sk2, reason, strlen(reason));
+ }
+ exit(0);
+ }
+
+ DTRACE("Created process %d", pid);
+ if (conn)
+ conn->pid = pid;
+ close(sk2);
+ }
+}
+
+#else
+
+static void run_as_daemon(int do_fork UNUSED)
+{
+ die("Daemon mode not supported in this version, need to recompile.");
+}
+
+static void setproctitle_init(int argc UNUSED, char **argv UNUSED)
+{
+}
+
+#endif
+
+static void init_memory(int set_password)
+{
+ mem = malloc(memblocks * sizeof(word *));
+ for (int i=0; i<memblocks; i++)
+ mem[i] = malloc(MEM_SIZE * sizeof(word));
+
+ if (set_password)
+ {
+ // For the contest, we fill the whole memory with -00 00 0000 0000 (HALT),
+ // not +00 00 0000 0000 (NOP). Otherwise, an empty program would reveal
+ // the location of the password :)
+ for (int i=0; i<memblocks; i++)
+ for (int j=0; j<MEM_SIZE; j++)
+ mem[i][j] = 01000000000000ULL;
+
+ // Store the password
+ int pos = 02655;
+ mem[0][pos++] = 0574060565373;
+ mem[0][pos++] = 0371741405340;
+ mem[0][pos++] = 0534051524017;
+ }
+ else
+ for (int i=0; i<memblocks; i++)
+ for (int j=0; j<MEM_SIZE; j++)
+ mem[i][j] = 00000000000000ULL;
+}
+
+static const struct option longopts[] = {
+ { "cpu-quota", required_argument, NULL, 'q' },
+ { "daemon", no_argument, NULL, 'd' },
+ { "nofork", no_argument, NULL, 'n' },
+ { "english", no_argument, NULL, 'e' },
+ { "set-password", no_argument, NULL, 's' },
+ { "upgrade", no_argument, NULL, 'u' },
+ { "print-quota", required_argument, NULL, 'p' },
+ { "trace", required_argument, NULL, 't' },
+ { NULL, 0, NULL, 0 },
+};
+
+static void usage(void)
+{
+ fprintf(stderr, "Options:\n\n");
+ #ifdef ENABLE_DAEMON_MODE
+ fprintf(stderr, "\
+-d, --daemon Run as daemon and listen for network connections\n\
+-n, --nofork When run with --daemon, avoid forking\n\
+");
+ #endif
+ fprintf(stderr, "\
+-e, --english Print messages in English\n\
+-s, --set-password Put hidden password in memory\n\
+-u, --upgrade Upgrade the Minsk-2 to the Minsk-22\n\
+-t, --trace=<level> Enable tracing of program execution\n\
+-q, --cpu-quota=<n> Set CPU quota to <n> instructions\n\
+-p, --print-quota=<n> Set printer quota to <n> lines\n\
+");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int daemon_mode = 0;
+ int do_fork = 1;
+ int set_password = 0;
+
+ while ((opt = getopt_long(argc, argv, "q:desunp:t:", longopts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'd':
+ daemon_mode = 1;
+ break;
+ case 'n':
+ do_fork = 0;
+ break;
+ case 'e':
+ english = 1;
+ break;
+ case 's':
+ set_password = 1;
+ break;
+ case 'u':
+ memblocks = 2;
+ break;
+ case 'p':
+ print_quota = atoi(optarg);
+ break;
+ case 'q':
+ cpu_quota = atoi(optarg);
+ break;
+ case 't':
+ trace = atoi(optarg);
+ break;
+ default:
+ usage();
+ }
+ if (optind < argc)
+ usage();
+
+ setproctitle_init(argc, argv);
+ init_memory(set_password);
+
+ if (daemon_mode)
+ run_as_daemon(do_fork);
+