4 * (c) 2010 Martin Mares <mj@ucw.cz>
8 * Things that are not implemented:
11 * - exact behavior of accumulator/R1/R2 (the manual lacks details)
12 * - exact behavior of negative zero
13 * - I/O instructions for devices that are not emulated (paper tape
14 * reader and puncher, card reader and puncher, magnetic tape unit)
18 #define UNUSED __attribute__((unused))
19 #define NORETURN __attribute__((noreturn))
21 #undef ENABLE_DAEMON_MODE
33 static int cpu_quota = -1;
34 static int print_quota = -1;
36 static int memblocks = 1;
37 static void (*error_hook)(char *msg);
39 // Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
40 typedef unsigned long long int word;
43 #define WORD_MASK 01777777777777ULL
44 #define SIGN_MASK 01000000000000ULL
45 #define VAL_MASK 00777777777777ULL
53 static int wsign(word w)
55 return (w & SIGN_MASK) ? -1 : 1;
58 static word wabs(word w)
63 #define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
64 #define LF(a) (a.block), (a.address)
66 static long long wtoll(word w)
74 static word wfromll(long long x)
76 word w = ((x < 0) ? -x : x) & VAL_MASK;
82 static double wtofrac(word w)
84 return (double)wtoll(w) / (double)(1ULL << 36);
87 static word wfromfrac(double d)
89 return wfromll((long long)(d * (double)(1ULL << 36)));
92 static int int_in_range(long long x)
94 return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
97 static int frac_in_range(double d)
99 return (d > -1. && d < 1.);
102 static int wexp(word w)
105 return (w & 0100 ? -exp : exp);
108 static word wputexp(word w, int exp)
110 return ((w & ~(word)0177) | ((exp < 0) ? 0100 | (-exp) : exp));
113 static int wmanti(word w)
115 return ((w >> 8) & ((1 << 28) - 1));
118 static double wtofloat(word w)
120 double x = wmanti(w);
121 return ldexp(x, wexp(w) - 28);
124 static int float_in_range(double x)
127 return (x <= ldexp((1 << 28) - 1, 63 - 28));
130 static word wfromfloat(double x, int normalized)
139 double m = frexp(x, &exp);
140 word mm = (word) ldexp(m, 28);
145 if (normalized || exp < -91)
165 static word rd(loc addr)
167 word val = addr.address ? mem[addr.block][addr.address] : 0;
169 printf("\tRD %d:%04o = %c%012llo\n", LF(addr), WF(val));
173 static void wr(loc addr, word val)
175 assert(!(val & ~(WORD_MASK)));
177 printf("\tWR %d:%04o = %c%012llo\n", LF(addr), WF(val));
178 mem[addr.block][addr.address] = val;
183 NORETURN static void parse_error(char *russian_msg, char *english_msg)
186 error_hook("Parse error");
189 printf("Parse error (line %d): %s\n", lino, english_msg);
191 printf("Ошибка входа (стр. %d): %s\n", lino, russian_msg);
195 static void parse_in(void)
200 while (fgets(line, sizeof(line), stdin))
203 char *eol = strchr(line, '\n');
205 parse_error("Строка слишком долгая", "Line too long");
207 if (eol > line && eol[-1] == '\r')
211 if (!c[0] || c[0] == ';')
221 for (int i=0; i<4; i++)
225 if (*c >= '0' && *c <= '7')
226 addr.address = 8*addr.address + *c++ - '0';
228 parse_error("Плохая цифра", "Invalid number");
233 parse_error("Адрес слишком долгий", "Address too long");
241 parse_error("Плохой знак", "Invalid sign");
243 for (int i=0; i<12; i++)
247 if (*c >= '0' && *c <= '7')
248 w = 8*w + *c++ - '0';
250 parse_error("Плохая цифра", "Invalid number");
255 parse_error("Номер слишком долгий", "Number too long");
257 addr.address = (addr.address+1) & 07777;
262 static word r1, r2, current_ins;
263 static int ip = 00050; // Standard program start location
266 NORETURN static void stop(char *russian_reason, char *english_reason)
269 error_hook(english_reason);
273 printf("System stopped -- %s\n", english_reason);
274 printf("IP:%04o ACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
278 printf("Машина остановлена -- %s\n", russian_reason);
279 printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
284 NORETURN static void over(void)
286 stop("Аварийный останов", "Overflow");
289 NORETURN static void notimp(void)
292 stop("Устройство разбитое", "Not implemented");
295 NORETURN static void noins(void)
298 stop("Эту команду не знаю", "Illegal instruction");
301 static uint16_t linebuf[128];
303 static uint16_t russian_chars[64] = {
304 '0', '1', '2', '3', '4', '5', '6', '7', // 0x
305 '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
306 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
307 ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
308 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, // 4x
309 0x418, 0x419, 0x41a, 0x41b, 0x41c, 0x41d, 0x41e, 0x41f, // 5x
310 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, // 6x
311 0x428, 0x429, 0x42b, 0x42c, 0x42d, 0x42e, 0x42f, 0x2013 // 7x
314 static uint16_t latin_chars[64] = {
315 '0', '1', '2', '3', '4', '5', '6', '7', // 0x
316 '8', '9', '+', '-', '/', ',', '.', ' ', // 1x
317 0x2169, '^', '(', ')', 0x00d7, '=', ';', '[', // 2x
318 ']', '*', '`', '\'', 0x2260, '<', '>', ':', // 3x
319 'A', 'B', 'W', 'G', 'D', 'E', 'V', 'Z', // 4x
320 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 5x
321 'R', 'S', 'T', 'U', 'F', 'H', 'C', ' ', // 6x
322 ' ', ' ', 'Y', 'X', ' ', ' ', 'Q', 0x2013 // 7x
325 static void print_line(int r)
328 * Meaning of bits of r:
329 * 0 = perform line feed
335 if (print_quota > 0 && !--print_quota)
336 stop("Бумага дошла - нужно ехать в Сибирь про новую", "Out of paper");
337 for (int i=0; i<128; i++)
346 putchar(0xc0 | (ch >> 6));
347 putchar(0x80 | (ch & 0x3f));
351 putchar(0xe0 | (ch >> 12));
352 putchar(0x80 | ((ch >> 6) & 0x3f));
353 putchar(0x80 | (ch & 0x3f));
358 memset(linebuf, 0, sizeof(linebuf));
366 static void print_ins(int x, loc y)
370 int r = (x >> 9) & 7;
383 case 0: // Decimal float
384 fmt = "+dddddddx+xbd";
386 case 1: // Octal number
387 fmt = "+oooooooooooo";
389 case 2: // Decimal fixed
392 case 3: // Decimal unsigned
396 case 4: // One Russian symbol
399 case 5: // Russian text
402 case 6: // One Latin symbol
405 default: // Latin text
422 ch = (yy & (1ULL << bit)) ? '-' : '+';
426 ch = '0' + ((yy >> bit) & 1);
430 ch = '0' + ((yy >> bit) & 7);
434 ch = '0' + ((yy >> bit) & 15);
440 ch = russian_chars[(yy >> bit) & 077];
444 ch = latin_chars[(yy >> bit) & 077];
452 if (ch == '0' || ch == ' ')
458 pos = (pos+1) & 0177;
463 static void run(void)
472 int op = (w >> 30) & 0177; // Operation code
473 int ax = (w >> 28) & 3; // Address extensions supported in Minsk-22 mode
474 int ix = (w >> 24) & 15; // Indexing
475 loc x = { ax >> 1, (w >> 12) & 07777 }; // Operands (original form)
476 loc y = { ax & 1, w & 07777 };
477 loc xi=x, yi=y; // (indexed form)
479 printf("@%04o %c%02o %02o %d:%04o %d:%04o\n",
481 (w & SIGN_MASK) ? '-' : '+',
482 (int)((w >> 30) & 077),
483 (int)((w >> 24) & 077),
490 loc iaddr = { 0, ix };
492 xi.address = (xi.address + (int)((i >> 12) & 07777)) & 07777;
493 yi.address = (yi.address + (int)(i & 07777)) & 07777;
495 printf("\tIndexing -> %d:%04o %d:%04o\n", LF(xi), LF(yi));
500 if (cpu_quota > 0 && !--cpu_quota)
501 stop("Тайм-аут", "CPU quota exceeded");
503 /* Arithmetic operations */
506 long long aa, bb, cc;
510 auto void afetch(void);
520 auto void astore(word result);
521 void astore(word result)
528 auto void astore_int(long long x);
529 void astore_int(long long x)
531 if (!int_in_range(x))
536 auto void astore_frac(double f);
537 void astore_frac(double f)
539 if (!frac_in_range(f))
541 astore(wfromfrac(f));
544 auto void astore_float(double f);
545 void astore_float(double f)
547 if (!float_in_range(f))
549 astore(wfromfloat(f, 0));
552 if (ax && memblocks == 1) // Reject address extensions if we only have 1 memory block
558 case 004 ... 007: // XOR
562 case 010 ... 013: // FIX addition
564 astore_int(wtoll(a) + wtoll(b));
566 case 014 ... 017: // FP addition
568 astore_float(wtofloat(a) + wtofloat(b));
570 case 020 ... 023: // FIX subtraction
572 astore_int(wtoll(a) - wtoll(b));
574 case 024 ... 027: // FP subtraction
576 astore_float(wtofloat(a) - wtofloat(b));
578 case 030 ... 033: // FIX multiplication
580 astore_frac(wtofrac(a) * wtofrac(b));
582 case 034 ... 037: // FP multiplication
584 astore_float(wtofloat(a) * wtofloat(b));
586 case 040 ... 043: // FIX division
592 astore_frac(ad / bd);
594 case 044 ... 047: // FP division
598 if (!bd || wexp(b) < -63)
600 astore_float(ad / bd);
602 case 050 ... 053: // FIX subtraction of abs values
604 astore_int(wabs(a) - wabs(b));
606 case 054 ... 057: // FP subtraction of abs values
608 astore_float(fabs(wtofloat(a)) - fabs(wtofloat(b)));
610 case 060 ... 063: // Shift logical
613 if (i <= -37 || i >= 37)
616 astore((a << i) & WORD_MASK);
620 case 064 ... 067: // Shift arithmetical
624 if (i <= -36 || i >= 36)
627 cc = (aa << i) & VAL_MASK;
630 astore((a & SIGN_MASK) | wfromll(cc));
632 case 070 ... 073: // And
636 case 074 ... 077: // Or
644 stop("Останов машины", "Halted");
645 case 0103: // I/O magtape
647 case 0104: // Disable rounding
649 case 0105: // Enable rounding
651 case 0106: // Interrupt control
653 case 0107: // Reverse tape
656 wr(yi, r1 = acc = rd(xi));
658 case 0111: // Move negative
659 wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
661 case 0112: // Move absolute value
662 wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
664 case 0113: // Read from keyboard
666 case 0114: // Copy sign
667 wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
669 case 0115: // Read code from R1 (obscure)
671 case 0116: // Copy exponent
672 wr(yi, acc = wputexp(rd(yi), wexp(r1 = rd(xi))));
674 case 0117: // I/O teletype
679 loc iaddr = { 0, ix };
681 aa = (a >> 24) & 017777;
684 b = rd(y); // (a mountain range near Prague)
685 acc = ((aa-1) << 24) |
686 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
687 (((a & 07777) + (b & 07777)) & 07777);
695 case 0131: // Jump to subroutine
696 wr(y, acc = ((0130ULL << 30) | ((ip & 07777ULL) << 12)));
699 case 0132: // Jump if positive
705 case 0133: // Jump if overflow
706 // Since we always trap on overflow, this instruction always jumps to the 1st address
709 case 0134: // Jump if zero
715 case 0135: // Jump if key pressed
716 // No keys are ever pressed, so always jump to 2nd
719 case 0136: // Interrupt masking
721 case 0137: // Used only when reading from tape
723 case 0140 ... 0147: // I/O
725 case 0150 ... 0154: // I/O
727 case 0160 ... 0161: // I/O
729 case 0162: // Printing
730 print_ins(x.address, y);
734 case 0170: // FIX multiplication, bottom part
736 if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
738 acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
739 // XXX: What should be the sign? The book does not define that.
752 case 0172: // Add exponents
755 i = wexp(a) + wexp(b);
756 if (i < -63 || i > 63)
761 case 0173: // Sub exponents
764 i = wexp(b) - wexp(a);
765 if (i < -63 || i > 63)
770 case 0174: // Addition in one's complement
777 // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
780 case 0175: // Normalization
785 loc yinc = { yi.block, (yi.address+1) & 07777 };
794 while (!(a & (SIGN_MASK >> 1)))
801 loc yinc = { yi.block, (yi.address+1) & 07777 };
805 case 0176: // Population count
808 for (int i=0; i<36; i++)
811 // XXX: Guessing that acc gets a copy of the result
820 printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", WF(acc), WF(r1), WF(r2));
824 NORETURN static void die(char *msg)
826 fprintf(stderr, "minsk: %s\n", msg);
830 /*** Daemon interface ***/
832 #ifdef ENABLE_DAEMON_MODE
835 * The daemon mode was a quick hack for the Po drate contest.
836 * Most parameters are hard-wired.
843 #include <sys/signal.h>
844 #include <sys/wait.h>
845 #include <sys/poll.h>
846 #include <sys/socket.h>
847 #include <netinet/in.h>
848 #include <arpa/inet.h>
851 #define DTRACE(msg, args...) fprintf(stderr, msg "\n", ##args)
852 #define DLOG(msg, args...) fprintf(stderr, msg "\n", ##args)
854 #define DTRACE(msg, args...) do { } while(0)
855 #define DLOG(msg, args...) syslog(LOG_INFO, msg, ##args)
858 #define MAX_CONNECTIONS 50 // Per daemon
859 #define MAX_CONNS_PER_IP 1 // Per IP
860 #define MAX_TRACKERS 200 // IP address trackers
861 #define TBF_MAX 5 // Max number of tokens in the bucket
862 #define TBF_REFILL_PER_SEC 0.2 // Bucket refill rate (buckets/sec)
864 #define PID_FILE "/var/run/pd-minsk.pid"
868 static char **spt_argv;
869 static char *spt_start, *spt_end;
871 static void setproctitle_init(int argc, char **argv)
874 char **env, **oldenv, *t;
878 /* Create a backup copy of environment */
881 for (i=0; oldenv[i]; i++)
882 len += strlen(oldenv[i]) + 1;
883 __environ = env = malloc(sizeof(char *)*(i+1));
885 if (!__environ || !t)
886 die("malloc failed");
887 for (i=0; oldenv[i]; i++)
890 len = strlen(oldenv[i]) + 1;
891 memcpy(t, oldenv[i], len);
896 /* Scan for consecutive free space */
897 spt_start = spt_end = argv[0];
898 for (i=0; i<argc; i++)
899 if (!i || spt_end+1 == argv[i])
900 spt_end = argv[i] + strlen(argv[i]);
901 for (i=0; oldenv[i]; i++)
902 if (spt_end+1 == oldenv[i])
903 spt_end = oldenv[i] + strlen(oldenv[i]);
907 setproctitle(const char *msg, ...)
914 if (spt_end > spt_start)
916 n = vsnprintf(buf, sizeof(buf), msg, args);
917 if (n >= (int) sizeof(buf) || n < 0)
918 sprintf(buf, "<too-long>");
919 n = spt_end - spt_start;
920 strncpy(spt_start, buf, n);
922 spt_argv[0] = spt_start;
928 static void sigchld_handler(int sig UNUSED)
932 static void sigalrm_handler(int sig UNUSED)
934 const char err[] = "--- Timed out. Time machine disconnected. ---\n";
935 write(1, err, sizeof(err));
936 DLOG("Connection timed out");
940 static void child_error_hook(char *err)
942 DLOG("Stopped: %s", err);
945 static void child(int sk2)
951 struct sigaction sact = {
952 .sa_handler = sigalrm_handler,
954 if (sigaction(SIGALRM, &sact, NULL) < 0)
955 die("sigaction: %m");
962 const char welcome[] = "+++ Welcome to our computer museum. +++\n+++ Our time machine will connect you to one of our exhibits. +++\n\n";
963 write(1, welcome, sizeof(welcome));
965 error_hook = child_error_hook;
975 struct tracker *tracker;
978 static struct conn connections[MAX_CONNECTIONS];
980 static struct conn *get_conn(struct in_addr *a)
982 for (int i=0; i<MAX_CONNECTIONS; i++)
984 struct conn *c = &connections[i];
987 memcpy(&c->addr, a, sizeof(struct in_addr));
994 static struct conn *pid_to_conn(pid_t pid)
996 for (int i=0; i<MAX_CONNECTIONS; i++)
998 struct conn *c = &connections[i];
1005 static void put_conn(struct conn *c)
1012 struct in_addr addr;
1018 static struct tracker trackers[MAX_TRACKERS];
1020 static int get_tracker(struct conn *c)
1023 time_t now = time(NULL);
1026 for (i=0; i<MAX_TRACKERS; i++)
1029 if (!memcmp(&t->addr, &c->addr, sizeof(struct in_addr)))
1032 if (i < MAX_TRACKERS)
1034 if (now > t->last_access)
1036 t->tokens += (now - t->last_access) * (double) TBF_REFILL_PER_SEC;
1037 t->last_access = now;
1038 if (t->tokens > TBF_MAX)
1039 t->tokens = TBF_MAX;
1041 DTRACE("TBF: Using tracker %d (%.3f tokens)", i, t->tokens);
1046 for (int i=0; i<MAX_TRACKERS; i++)
1049 if (!t->active_conns && (min_i < 0 || t->last_access < trackers[min_i].last_access))
1054 DLOG("TBF: Out of trackers!");
1057 t = &trackers[min_i];
1059 DTRACE("TBF: Recycling tracker %d", min_i);
1061 DTRACE("TBF: Creating tracker %d", min_i);
1062 memset(t, 0, sizeof(*t));
1064 t->last_access = now;
1065 t->tokens = TBF_MAX;
1068 if (t->active_conns >= MAX_CONNS_PER_IP)
1070 DTRACE("TBF: Too many conns per IP");
1074 if (t->tokens >= 0.999)
1079 DTRACE("TBF: Passed (%d conns)", t->active_conns);
1084 DTRACE("TBF: Failed");
1090 static void put_tracker(struct conn *c)
1092 struct tracker *t = c->tracker;
1095 DLOG("put_tracker: no tracker?");
1099 if (t->active_conns <= 0)
1101 DLOG("put_tracker: no counter?");
1106 DTRACE("TBF: Put tracker (%d conns remain)", t->active_conns);
1109 static void run_as_daemon(int do_fork)
1111 int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1116 if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
1117 die("setsockopt: %m");
1119 struct sockaddr_in sa = {
1120 .sin_family = AF_INET,
1121 .sin_port = ntohs(1969),
1122 .sin_addr.s_addr = INADDR_ANY,
1124 if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
1126 if (listen(sk, 128) < 0)
1128 // if (fcntl(sk, F_SETFL, O_NONBLOCK) < 0)
1129 // die("fcntl: %m");
1138 FILE *f = fopen(PID_FILE, "w");
1141 fprintf(f, "%d\n", pid);
1148 setresgid(GID, GID, GID);
1149 setresuid(UID, UID, UID);
1153 struct sigaction sact = {
1154 .sa_handler = sigchld_handler,
1155 .sa_flags = SA_RESTART,
1157 if (sigaction(SIGCHLD, &sact, NULL) < 0)
1158 die("sigaction: %m");
1160 DLOG("Daemon ready");
1161 setproctitle("minsk: Listening");
1162 openlog("minsk", LOG_PID, LOG_LOCAL7);
1166 struct pollfd pfd[1] = {
1167 { .fd = sk, .events = POLLIN },
1170 int nfds = poll(pfd, 1, 60000);
1171 if (nfds < 0 && errno != EINTR)
1180 while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1182 if (!WIFEXITED(status) || WEXITSTATUS(status))
1183 DLOG("Process %d exited with strange status %x", pid, status);
1185 struct conn *conn = pid_to_conn(pid);
1188 DTRACE("Connection with PID %d exited", pid);
1193 DTRACE("PID %d exited, matching no connection", pid);
1196 if (!(pfd[0].revents & POLLIN))
1199 socklen_t salen = sizeof(sa);
1200 int sk2 = accept(sk, (struct sockaddr *) &sa, &salen);
1210 DTRACE("Got connection: fd=%d", sk2);
1212 struct conn *conn = get_conn(&sa.sin_addr);
1213 const char *reason = NULL;
1216 if (!get_tracker(conn))
1218 DLOG("Connection from %s dropped: Throttling", inet_ntoa(sa.sin_addr));
1221 reason = "--- Sorry, but you are sending too many requests. Please slow down. ---\n";
1226 DLOG("Connection from %s dropped: Too many connections", inet_ntoa(sa.sin_addr));
1227 reason = "--- Sorry, maximum number of connections exceeded. Please come later. ---\n";
1233 DLOG("fork failed: %m");
1242 DLOG("Accepted connection from %s", inet_ntoa(sa.sin_addr));
1243 setproctitle("minsk: %s", inet_ntoa(sa.sin_addr));
1248 DLOG("Sending error message to %s", inet_ntoa(sa.sin_addr));
1249 setproctitle("minsk: %s ERR", inet_ntoa(sa.sin_addr));
1250 write(sk2, reason, strlen(reason));
1255 DTRACE("Created process %d", pid);
1264 static void run_as_daemon(int do_fork UNUSED)
1266 die("Daemon mode not supported in this version, need to recompile.");
1269 static void setproctitle_init(int argc UNUSED, char **argv UNUSED)
1275 static void init_memory(int set_password)
1277 mem = malloc(memblocks * sizeof(word *));
1278 for (int i=0; i<memblocks; i++)
1279 mem[i] = malloc(MEM_SIZE * sizeof(word));
1283 // For the contest, we fill the whole memory with -00 00 0000 0000 (HALT),
1284 // not +00 00 0000 0000 (NOP). Otherwise, an empty program would reveal
1285 // the location of the password :)
1286 for (int i=0; i<memblocks; i++)
1287 for (int j=0; i<MEM_SIZE; j++)
1288 mem[i][j] = 01000000000000ULL;
1290 // Store the password
1292 mem[0][pos++] = 0574060565373;
1293 mem[0][pos++] = 0371741405340;
1294 mem[0][pos++] = 0534051524017;
1297 for (int i=0; i<memblocks; i++)
1298 for (int j=0; j<MEM_SIZE; j++)
1299 mem[i][j] = 00000000000000ULL;
1302 static const struct option longopts[] = {
1303 { "cpu-quota", required_argument, NULL, 'q' },
1304 { "daemon", no_argument, NULL, 'd' },
1305 { "nofork", no_argument, NULL, 'n' },
1306 { "english", no_argument, NULL, 'e' },
1307 { "set-password", no_argument, NULL, 's' },
1308 { "upgrade", no_argument, NULL, 'u' },
1309 { "print-quota", required_argument, NULL, 'p' },
1310 { "trace", required_argument, NULL, 't' },
1311 { NULL, 0, NULL, 0 },
1314 static void usage(void)
1316 fprintf(stderr, "Options:\n\n");
1317 #ifdef ENABLE_DAEMON_MODE
1319 -d, --daemon Run as daemon and listen for network connections\n\
1320 -n, --nofork When run with --daemon, avoid forking\n\
1324 -e, --english Print messages in English\n\
1325 -s, --set-password Put hidden password in memory\n\
1326 -u, --upgrade Upgrade the Minsk-2 to the Minsk-22\n\
1327 -t, --trace=<level> Enable tracing of program execution\n\
1328 -q, --cpu-quota=<n> Set CPU quota to <n> instructions\n\
1329 -p, --print-quota=<n> Set printer quota to <n> lines\n\
1334 int main(int argc, char **argv)
1337 int daemon_mode = 0;
1339 int set_password = 0;
1341 while ((opt = getopt_long(argc, argv, "q:desunp:t:", longopts, NULL)) >= 0)
1360 print_quota = atoi(optarg);
1363 cpu_quota = atoi(optarg);
1366 trace = atoi(optarg);
1374 setproctitle_init(argc, argv);
1375 init_memory(set_password);
1378 run_as_daemon(do_fork);