]> mj.ucw.cz Git - minsk.git/blob - minsk.c
Minsk: Added a backdoor :)
[minsk.git] / minsk.c
1 /*
2  *      Minsk-2 Emulator
3  *
4  *      (c) 2010 Martin Mares <mj@ucw.cz>
5  */
6
7 /*
8  * TODO:
9  *      - we probably have to disable NOP
10  */
11
12 /*
13  * Things that are not implemented:
14  *
15  *      - rounding modes
16  *      - exact behavior of accumulator/R1/R2 (the manual lacks details)
17  *      - exact behavior of negative zero
18  *      - I/O instructions for devices that are not emulated (paper tape
19  *        reader and puncher, card reader and puncher, magnetic tape unit)
20  */
21
22 #define _GNU_SOURCE
23
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <inttypes.h>
29 #include <assert.h>
30 #include <math.h>
31
32 static int trace;
33 static int cpu_quota = -1;
34 static int print_quota = -1;
35 static void (*error_hook)(char *msg);
36
37 // Minsk-2 has 37-bit words in sign-magnitude representation (bit 36 = sign)
38 typedef unsigned long long int word;
39
40 #define WORD_MASK 01777777777777ULL
41 #define SIGN_MASK 01000000000000ULL
42 #define  VAL_MASK 00777777777777ULL
43
44 static int wsign(word w)
45 {
46   return (w & SIGN_MASK) ? -1 : 1;
47 }
48
49 static word wabs(word w)
50 {
51   return w & VAL_MASK;
52 }
53
54 #define WF(w) (wsign(w) < 0 ? '-' : '+'), wabs(w)
55
56 static long long wtoll(word w)
57 {
58   if (wsign(w) < 0)
59     return -wabs(w);
60   else
61     return wabs(w);
62 }
63
64 static word wfromll(long long x)
65 {
66   word w = ((x < 0) ? -x : x) & VAL_MASK;
67   if (x < 0)
68     w |= SIGN_MASK;
69   return w;
70 }
71
72 static double wtofrac(word w)
73 {
74   return (double)wtoll(w) / (double)(1ULL << 36);
75 }
76
77 static word wfromfrac(double d)
78 {
79   return wfromll((long long)(d * (double)(1ULL << 36)));
80 }
81
82 static int int_in_range(long long x)
83 {
84   return (x >= -(long long)VAL_MASK && x <= (long long)VAL_MASK);
85 }
86
87 static int frac_in_range(double d)
88 {
89   return (d > -1. && d < 1.);
90 }
91
92 static int wexp(word w)
93 {
94   int exp = w & 077;
95   return (w & 0100 ? -exp : exp);
96 }
97
98 static word wputexp(word w, int exp)
99 {
100   return ((w & ~(word)0177) | ((exp < 0) ? 0100 | (-exp) : exp));
101 }
102
103 static int wmanti(word w)
104 {
105   return ((w >> 8) & ((1 << 28) - 1));
106 }
107
108 static double wtofloat(word w)
109 {
110   double x = wmanti(w);
111   return ldexp(x, wexp(w) - 28);
112 }
113
114 static int float_in_range(double x)
115 {
116   x = fabs(x);
117   return (x <= ldexp((1 << 28) - 1, 63 - 28));
118 }
119
120 static word wfromfloat(double x, int normalized)
121 {
122   word w = 0;
123   if (x < 0)
124     {
125       w |= SIGN_MASK;
126       x = -x;
127     }
128   int exp;
129   double m = frexp(x, &exp);
130   word mm = (word) ldexp(m, 28);
131   if (exp > 63)
132     assert(0);
133   else if (exp < -63)
134     {
135       if (normalized || exp < -91)
136         mm=0, exp=0;
137       else
138         {
139           mm >>= -exp - 63;
140           exp = -63;
141         }
142     }
143   w |= mm << 8;
144   if (exp < 0)
145     {
146       w |= 0100;
147       exp = -exp;
148     }
149   w |= exp;
150   return w;
151 }
152
153 static word mem[4096];
154
155 static word rd(int addr)
156 {
157   word val = addr ? mem[addr] : 0;
158   if (trace > 2)
159     printf("\tRD %04o = %c%012llo\n", addr, WF(val));
160   return val;
161 }
162
163 static void wr(int addr, word val)
164 {
165   assert(!(val & ~(WORD_MASK)));
166   if (trace > 2)
167     printf("\tWR %04o = %c%012llo\n", addr, WF(val));
168   mem[addr] = val;
169 }
170
171 static int lino;
172
173 static void parse_error(char *msg)
174 {
175   if (error_hook)
176     error_hook("Parse error");
177   printf("Ошибка входа (стр. %d): %s\n", lino, msg);
178   exit(0);
179 }
180
181 static void parse_in(void)
182 {
183   char line[80];
184   int addr = 0;
185
186   while (fgets(line, sizeof(line), stdin))
187     {
188       lino++;
189       char *eol = strchr(line, '\n');
190       if (!eol)
191         parse_error("Строка слишком долгая");
192       *eol = 0;
193
194       char *c = line;
195       if (!c[0] || c[0] == ';')
196         {
197           if (!strncmp(c, ";daji_zor_i_litva=", 18))
198             {
199               trace = atoi(c+18);
200               if (error_hook)
201                 error_hook("Secret tracing switch flipped");
202             }
203           continue;
204         }
205
206       if (c[0] == '@')
207         {
208           c++;
209           addr = 0;
210           for (int i=0; i<4; i++)
211             {
212               while (*c == ' ')
213                 c++;
214               if (*c >= '0' && *c <= '7')
215                 addr = 8*addr + *c++ - '0';
216               else
217                 parse_error("Плохая цифра");
218             }
219           while (*c == ' ')
220             c++;
221           if (*c)
222             parse_error("Адрес слишком долгий");
223           continue;
224         }
225
226       word w = 0;
227       if (*c == '-')
228         w = 1;
229       else if (*c != '+')
230         parse_error("Плохой знак");
231       c++;
232       for (int i=0; i<12; i++)
233         {
234           while (*c == ' ')
235             c++;
236           if (*c >= '0' && *c <= '7')
237             w = 8*w + *c++ - '0';
238           else
239             parse_error("Плохая цифра");
240         }
241       while (*c == ' ')
242         c++;
243       if (*c)
244         parse_error("Номер слишком долгий");
245       wr(addr++, w);
246       addr &= 07777;
247     }
248 }
249
250 static word acc;
251 static word r1, r2, current_ins;
252 static int ip = 00050;                  // Standard program start location
253 static int prev_ip;
254
255 static void stop(char *reason, char *notice)
256 {
257   if (error_hook)
258     error_hook(notice);
259   printf("Машина остановлена -- %s\n", reason);
260   printf("СчАК:%04o См:%c%012llo Р1:%c%012llo Р2:%c%012llo\n", prev_ip, WF(acc), WF(r1), WF(r2));
261   exit(0);
262 }
263
264 static void over(void)
265 {
266   stop("Аварийный останов", "Overflow");
267 }
268
269 static void notimp(void)
270 {
271   acc = current_ins;
272   stop("Устройство разбитое", "Not implemented");
273 }
274
275 static void noins(void)
276 {
277   acc = current_ins;
278   stop("Эту команду не знаю", "Illegal instruction");
279 }
280
281 static uint16_t linebuf[128];
282
283 static uint16_t russian_chars[64] = {
284         '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',    // 0x
285         '8',    '9',    '+',    '-',    '/',    ',',    '.',    ' ',    // 1x
286         0x2169, '^',    '(',    ')',    0x00d7, '=',    ';',    '[',    // 2x
287         ']',    '*',    '`',    '\'',   0x2260, '<',    '>',    ':',    // 3x
288         0x410,  0x411,  0x412,  0x413,  0x414,  0x415,  0x416,  0x417,  // 4x
289         0x418,  0x419,  0x41a,  0x41b,  0x41c,  0x41d,  0x41e,  0x41f,  // 5x
290         0x420,  0x421,  0x422,  0x423,  0x424,  0x425,  0x426,  0x427,  // 6x
291         0x428,  0x429,  0x42b,  0x42c,  0x42d,  0x42e,  0x42f,  0x2013  // 7x
292 };
293
294 static uint16_t latin_chars[64] = {
295         '0',    '1',    '2',    '3',    '4',    '5',    '6',    '7',    // 0x
296         '8',    '9',    '+',    '-',    '/',    ',',    '.',    ' ',    // 1x
297         0x2169, '^',    '(',    ')',    0x00d7, '=',    ';',    '[',    // 2x
298         ']',    '*',    '`',    '\'',   0x2260, '<',    '>',    ':',    // 3x
299         'A',    'B',    'W',    'G',    'D',    'E',    'V',    'Z',    // 4x
300         'I',    'J',    'K',    'L',    'M',    'N',    'O',    'P',    // 5x
301         'R',    'S',    'T',    'U',    'F',    'H',    'C',    ' ',    // 6x
302         ' ',    ' ',    'Y',    'X',    ' ',    ' ',    'Q',    0x2013  // 7x
303 };
304
305 static void print_line(int r)
306 {
307   /*
308    *  Meaning of bits of r:
309    *    0 = perform line feed
310    *    1 = clear buffer
311    *    2 = actually print
312    */
313   if (r & 4)
314     {
315       if (print_quota > 0 && !--print_quota)
316         stop("Бумага дошла - нужно ехать в Сибирь про новую", "Out of paper");
317       for (int i=0; i<128; i++)
318         {
319           int ch = linebuf[i];
320           if (!ch)
321             ch = ' ';
322           if (ch < 0x80)
323             putchar(ch);
324           else if (ch < 0x800)
325             {
326               putchar(0xc0 | (ch >> 6));
327               putchar(0x80 | (ch & 0x3f));
328             }
329           else
330             {
331               putchar(0xe0 | (ch >> 12));
332               putchar(0x80 | ((ch >> 6) & 0x3f));
333               putchar(0x80 | (ch & 0x3f));
334             }
335         }
336     }
337   if (r & 2)
338     memset(linebuf, 0, sizeof(linebuf));
339   if (r & 1)
340     putchar('\n');
341   else if (r & 4)
342     putchar('\r');
343   fflush(stdout);
344 }
345
346 static void print_ins(int x, int y)
347 {
348   word yy = rd(y);
349   int pos = x & 0177;
350   int r = (x >> 9) & 7;
351
352   if (x & 0400)
353     {
354       print_line(r);
355       return;
356     }
357
358   char *fmt;
359   int bit = 37;
360   int eat = 0;
361   switch (r)
362     {
363     case 0:                             // Decimal float
364       fmt = "+dddddddx+xbd";
365       break;
366     case 1:                             // Octal number
367       fmt = "+oooooooooooo";
368       break;
369     case 2:                             // Decimal fixed
370       fmt = "+ddddddddd";
371       break;
372     case 3:                             // Decimal unsigned
373       fmt = "x ddddddddd";
374       eat = 1;
375       break;
376     case 4:                             // One Russian symbol
377       bit = 6;
378       fmt = "r";
379       break;
380     case 5:                             // Russian text
381       fmt = "xrrrrrr";
382       break;
383     case 6:                             // One Latin symbol
384       bit = 6;
385       fmt = "l";
386       break;
387     default:                            // Latin text
388       fmt = "xllllll";
389     }
390
391   while (*fmt)
392     {
393       int ch;
394       switch (*fmt++)
395         {
396         case 'x':
397           bit--;
398           continue;
399         case ' ':
400           ch = ' ';
401           break;
402         case '+':
403           bit--;
404           ch = (yy & (1ULL << bit)) ? '-' : '+';
405           break;
406         case 'b':
407           bit--;
408           ch = '0' + ((yy >> bit) & 1);
409           break;
410         case 'o':
411           bit -= 3;
412           ch = '0' + ((yy >> bit) & 7);
413           break;
414         case 'd':
415           bit -= 4;
416           ch = '0' + ((yy >> bit) & 15);
417           if (ch > '0' + 9)
418             ch += 7;
419           break;
420         case 'r':
421           bit -= 6;
422           ch = russian_chars[(yy >> bit) & 077];
423           break;
424         case 'l':
425           bit -= 6;
426           ch = latin_chars[(yy >> bit) & 077];
427           break;
428         default:
429           assert(0);
430         }
431
432       if (eat && *fmt)
433         {
434           if (ch == '0' || ch == ' ')
435             ch = ' ';
436           else
437             eat = 0;
438         }
439       linebuf[pos] = ch;
440       pos = (pos+1) & 0177;
441     }
442   assert(!bit);
443 }
444
445 static void run(void)
446 {
447   for (;;)
448     {
449       r2 = acc;
450       prev_ip = ip;
451       word w = mem[ip];
452       current_ins = w;
453
454       int op = (w >> 30) & 0177;        // Operation code
455       int ax = (w >> 28) & 3;           // Address extensions not supported
456       int ix = (w >> 24) & 15;          // Indexing
457       int x = (w >> 12) & 07777;        // Operands (original form)
458       int y = w & 07777;
459       int xi=x, yi=y;                   // (indexed form)
460       if (trace)
461         printf("@%04o  %c%02o %02o %04o %04o\n",
462           ip,
463           (w & SIGN_MASK) ? '-' : '+',
464           (int)((w >> 30) & 077),
465           (int)((w >> 24) & 077),
466           x,
467           y);
468       if (ix)
469         {
470           if (op != 0120)
471             {
472               word i = rd(ix);
473               xi = (xi + (int)((i >> 12) & 07777)) & 07777;
474               yi = (yi + (int)(i & 07777)) & 07777;
475               if (trace > 2)
476                 printf("\tIndexing -> %04o %04o\n", xi, yi);
477             }
478         }
479       ip = (ip+1) & 07777;
480
481       if (cpu_quota > 0 && !--cpu_quota)
482         stop("Тайм-аут", "CPU quota exceeded");
483
484       /* Arithmetic operations */
485
486       word a, b, c;
487       long long aa, bb, cc;
488       double ad, bd;
489       int i;
490
491       auto void afetch(void);
492       void afetch(void)
493         {
494           if (op & 2)
495             a = r2;
496           else
497             a = rd(yi);
498           b = r1 = rd(xi);
499         }
500
501       auto void astore(word result);
502       void astore(word result)
503         {
504           acc = result;
505           if (op & 1)
506             wr(yi, acc);
507         }
508
509       auto void astore_int(long long x);
510       void astore_int(long long x)
511         {
512           if (!int_in_range(x))
513             over();
514           astore(wfromll(x));
515         }
516
517       auto void astore_frac(double f);
518       void astore_frac(double f)
519         {
520           if (!frac_in_range(f))
521             over();
522           astore(wfromfrac(f));
523         }
524
525       auto void astore_float(double f);
526       void astore_float(double f)
527         {
528           if (!float_in_range(f))
529             over();
530           astore(wfromfloat(f, 0));
531         }
532
533       if (ax)
534         op = -1;
535       switch (op)
536         {
537         case 000:               // NOP
538           break;
539         case 004 ... 007:       // XOR
540           afetch();
541           astore(a^b);
542           break;
543         case 010 ... 013:       // FIX addition
544           afetch();
545           astore_int(wtoll(a) + wtoll(b));
546           break;
547         case 014 ... 017:       // FP addition
548           afetch();
549           astore_float(wtofloat(a) + wtofloat(b));
550           break;
551         case 020 ... 023:       // FIX subtraction
552           afetch();
553           astore_int(wtoll(a) - wtoll(b));
554           break;
555         case 024 ... 027:       // FP subtraction
556           afetch();
557           astore_float(wtofloat(a) - wtofloat(b));
558           break;
559         case 030 ... 033:       // FIX multiplication
560           afetch();
561           astore_frac(wtofrac(a) * wtofrac(b));
562           break;
563         case 034 ... 037:       // FP multiplication
564           afetch();
565           astore_float(wtofloat(a) * wtofloat(b));
566           break;
567         case 040 ... 043:       // FIX division
568           afetch();
569           ad = wtofrac(a);
570           bd = wtofrac(b);
571           if (!wabs(b))
572             over();
573           astore_frac(ad / bd);
574           break;
575         case 044 ... 047:       // FP division
576           afetch();
577           ad = wtofloat(a);
578           bd = wtofloat(b);
579           if (!bd || wexp(b) < -63)
580             over();
581           astore_float(ad / bd);
582           break;
583         case 050 ... 053:       // FIX subtraction of abs values
584           afetch();
585           astore_int(wabs(a) - wabs(b));
586           break;
587         case 054 ... 057:       // FP subtraction of abs values
588           afetch();
589           astore_float(fabs(wtofloat(a)) - fabs(wtofloat(b)));
590           break;
591         case 060 ... 063:       // Shift logical
592           afetch();
593           i = wexp(b);
594           if (i <= -37 || i >= 37)
595             astore(0);
596           else if (i >= 0)
597             astore((a << i) & WORD_MASK);
598           else
599             astore(a >> (-i));
600           break;
601         case 064 ... 067:       // Shift arithmetical
602           afetch();
603           i = wexp(b);
604           aa = wabs(a);
605           if (i <= -36 || i >= 36)
606             cc = 0;
607           else if (i >= 0)
608             cc = (aa << i) & VAL_MASK;
609           else
610             cc = aa >> (-i);
611           astore((a & SIGN_MASK) | wfromll(cc));
612           break;
613         case 070 ... 073:       // And
614           afetch();
615           astore(a&b);
616           break;
617         case 074 ... 077:       // Or
618           afetch();
619           astore(a|b);
620           break;
621
622         case 0100:              // Halt
623           r1 = rd(x);
624           acc = rd(y);
625           stop("Останов машины", "Halted");
626         case 0103:              // I/O magtape
627           notimp();
628         case 0104:              // Disable rounding
629           notimp();
630         case 0105:              // Enable rounding
631           notimp();
632         case 0106:              // Interrupt control
633           notimp();
634         case 0107:              // Reverse tape
635           notimp();
636         case 0110:              // Move
637           wr(yi, r1 = acc = rd(xi));
638           break;
639         case 0111:              // Move negative
640           wr(yi, acc = (r1 = rd(xi)) ^ SIGN_MASK);
641           break;
642         case 0112:              // Move absolute value
643           wr(yi, acc = (r1 = rd(xi)) & VAL_MASK);
644           break;
645         case 0113:              // Read from keyboard
646           notimp();
647         case 0114:              // Copy sign
648           wr(yi, acc = rd(yi) ^ ((r1 = rd(xi)) & SIGN_MASK));
649           break;
650         case 0115:              // Read code from R1 (obscure)
651           notimp();
652         case 0116:              // Copy exponent
653           wr(yi, acc = wputexp(rd(yi), wexp(r1 = rd(xi))));
654           break;
655         case 0117:              // I/O teletype
656           notimp();
657         case 0120:              // Loop
658           if (!ix)
659             noins();
660           a = r1 = rd(ix);
661           aa = (a >> 24) & 017777;
662           if (!aa)
663             break;
664           b = rd(y);            // (a mountain range near Prague)
665           acc = ((aa-1) << 24) |
666                 (((((a >> 12) & 07777) + (b >> 12) & 07777) & 07777) << 12) |
667                 (((a & 07777) + (b & 07777)) & 07777);
668           wr(ix, acc);
669           ip = x;
670           break;
671         case 0130:              // Jump
672           wr(y, r2);
673           ip = x;
674           break;
675         case 0131:              // Jump to subroutine
676           wr(y, acc = ((030ULL << 30) | ((ip & 07777ULL) << 12)));
677           ip = x;
678           break;
679         case 0132:              // Jump if positive
680           if (wsign(r2) >= 0)
681             ip = x;
682           else
683             ip = y;
684           break;
685         case 0133:              // Jump if overflow
686           // Since we always trap on overflow, this instruction always jumps to the 1st address
687           ip = x;
688           break;
689         case 0134:              // Jump if zero
690           if (!wabs(r2))
691             ip = y;
692           else
693             ip = x;
694           break;
695         case 0135:              // Jump if key pressed
696           // No keys are ever pressed, so always jump to 2nd
697           ip = y;
698           break;
699         case 0136:              // Interrupt masking
700           notimp();
701         case 0137:              // Used only when reading from tape
702           notimp();
703         case 0140 ... 0147:     // I/O
704           notimp();
705         case 0150 ... 0154:     // I/O
706           notimp();
707         case 0160 ... 0161:     // I/O
708           notimp();
709         case 0162:              // Printing
710           print_ins(x, y);
711           break;
712         case 0163:              // I/O
713           notimp();
714         case 0170:              // FIX multiplication, bottom part
715           afetch();
716           if (wtofrac(a) * wtofrac(b) >= .1/(1ULL << 32))
717             over();
718           acc = wfromll(((unsigned long long)wabs(a) * (unsigned long long)wabs(b)) & VAL_MASK);
719           // XXX: What should be the sign? The book does not define that.
720           break;
721         case 0171:              // Modulo
722           afetch();
723           aa = wabs(a);
724           bb = wabs(b);
725           if (!bb)
726             over();
727           cc = aa % bb;
728           if (wsign(b) < 0)
729             cc = -cc;
730           acc = wfromll(cc);
731           break;
732         case 0172:              // Add exponents
733           a = r1 = rd(xi);
734           b = rd(yi);
735           i = wexp(a) + wexp(b);
736           if (i < -63 || i > 63)
737             over();
738           acc = wputexp(b, i);
739           wr(yi, acc);
740           break;
741         case 0173:              // Sub exponents
742           a = r1 = rd(xi);
743           b = rd(yi);
744           i = wexp(b) - wexp(a);
745           if (i < -63 || i > 63)
746             over();
747           acc = wputexp(b, i);
748           wr(yi, acc);
749           break;
750         case 0174:              // Addition in one's complement
751           a = r1 = rd(xi);
752           b = rd(yi);
753           c = a + b;
754           if (c > VAL_MASK)
755             c = c - VAL_MASK;
756           wr(yi, c);
757           // XXX: The effect on the accumulator is undocumented, but likely to be as follows:
758           acc = c;
759           break;
760         case 0175:              // Normalization
761           a = r1 = rd(xi);
762           if (!wabs(a))
763             {
764               wr(yi, 0);
765               wr((yi+1) & 07777, 0);
766               acc = 0;
767             }
768           else
769             {
770               i = 0;
771               acc = a & SIGN_MASK;
772               a &= VAL_MASK;
773               while (!(a & (SIGN_MASK >> 1)))
774                 {
775                   a <<= 1;
776                   i++;
777                 }
778               acc |= a;
779               wr(yi, acc);
780               wr((yi+1) & 07777, i);
781             }
782           break;
783         case 0176:              // Population count
784           a = r1 = rd(xi);
785           cc = 0;
786           for (int i=0; i<36; i++)
787             if (a & (1ULL << i))
788               cc++;
789           // XXX: Guessing that acc gets a copy of the result
790           acc = wfromll(cc);
791           wr(yi, acc);
792           break;
793         default:
794           noins();
795         }
796
797       if (trace > 1)
798         printf("\tACC:%c%012llo R1:%c%012llo R2:%c%012llo\n", WF(acc), WF(r1), WF(r2));
799     }
800 }
801
802 /*** Daemon interface ***/
803
804 #include <unistd.h>
805 #include <errno.h>
806 #include <time.h>
807 #include <syslog.h>
808 #include <sys/signal.h>
809 #include <sys/wait.h>
810 #include <sys/poll.h>
811 #include <sys/socket.h>
812 #include <netinet/in.h>
813 #include <arpa/inet.h>
814
815 #if 0
816 #define DTRACE(msg, args...) fprintf(stderr, msg "\n", ##args)
817 #define DLOG(msg, args...) fprintf(stderr, msg "\n", ##args)
818 #else
819 #define DTRACE(msg, args...) do { } while(0)
820 #define DLOG(msg, args...) syslog(LOG_INFO, msg, ##args)
821 #endif
822
823 #define MAX_CONNECTIONS 50              // Per daemon
824 #define MAX_CONNS_PER_IP 1              // Per IP
825 #define MAX_TRACKERS 200                // IP address trackers
826 #define TBF_MAX 5                       // Max number of tokens in the bucket
827 #define TBF_REFILL_PER_SEC 0.2          // Bucket refill rate (buckets/sec)
828
829 #define PID_FILE "/var/run/minsk.pid"
830 #define UID 65534
831 #define GID 65534
832
833 static void die(char *msg)
834 {
835   fprintf(stderr, "minsk: ");
836   fprintf(stderr, msg);
837   fputc('\n', stderr);
838   exit(1);
839 }
840
841 static char **spt_argv;
842 static char *spt_start, *spt_end;
843
844 static void setproctitle_init(int argc, char **argv)
845 {
846   int i, len;
847   char **env, **oldenv, *t;
848
849   spt_argv = argv;
850
851   /* Create a backup copy of environment */
852   oldenv = __environ;
853   len = 0;
854   for (i=0; oldenv[i]; i++)
855     len += strlen(oldenv[i]) + 1;
856   __environ = env = malloc(sizeof(char *)*(i+1));
857   t = malloc(len);
858   if (!__environ || !t)
859     die("malloc failed");
860   for (i=0; oldenv[i]; i++)
861     {
862       env[i] = t;
863       len = strlen(oldenv[i]) + 1;
864       memcpy(t, oldenv[i], len);
865       t += len;
866     }
867   env[i] = NULL;
868
869   /* Scan for consecutive free space */
870   spt_start = spt_end = argv[0];
871   for (i=0; i<argc; i++)
872     if (!i || spt_end+1 == argv[i])
873       spt_end = argv[i] + strlen(argv[i]);
874   for (i=0; oldenv[i]; i++)
875     if (spt_end+1 == oldenv[i])
876       spt_end = oldenv[i] + strlen(oldenv[i]);
877 }
878
879 static void
880 setproctitle(const char *msg, ...)
881 {
882   va_list args;
883   char buf[256];
884   int n;
885
886   va_start(args, msg);
887   if (spt_end > spt_start)
888     {
889       n = vsnprintf(buf, sizeof(buf), msg, args);
890       if (n >= (int) sizeof(buf) || n < 0)
891         sprintf(buf, "<too-long>");
892       n = spt_end - spt_start;
893       strncpy(spt_start, buf, n);
894       spt_start[n] = 0;
895       spt_argv[0] = spt_start;
896       spt_argv[1] = NULL;
897     }
898   va_end(args);
899 }
900
901 static void sigchld_handler(int sig __attribute__((unused)))
902 {
903 }
904
905 static void sigalrm_handler(int sig __attribute__((unused)))
906 {
907   const char err[] = "--- Timed out. Time machine disconnected. ---\n";
908   write(1, err, sizeof(err));
909   DLOG("Connection timed out");
910   exit(0);
911 }
912
913 static void child_error_hook(char *err)
914 {
915   DLOG("Stopped: %s", err);
916 }
917
918 static void child(int sk2)
919 {
920   dup2(sk2, 0);
921   dup2(sk2, 1);
922   close(sk2);
923
924   struct sigaction sact = {
925     .sa_handler = sigalrm_handler,
926   };
927   if (sigaction(SIGALRM, &sact, NULL) < 0)
928     die("sigaction: %m");
929
930   // Set up limits
931   alarm(60);
932   cpu_quota = 100000;
933   print_quota = 100;
934
935   const char welcome[] = "+++ Welcome to our computer museum. +++\n+++ Our time machine will connect you to one of our exhibits. +++\n\n";
936   write(1, welcome, sizeof(welcome));
937
938   error_hook = child_error_hook;
939   parse_in();
940   run();
941   fflush(stdout);
942   DTRACE("Finished");
943 }
944
945 struct conn {
946   pid_t pid;
947   struct in_addr addr;
948   struct tracker *tracker;
949 };
950
951 static struct conn connections[MAX_CONNECTIONS];
952
953 static struct conn *get_conn(struct in_addr *a)
954 {
955   for (int i=0; i<MAX_CONNECTIONS; i++)
956     {
957       struct conn *c = &connections[i];
958       if (!c->pid)
959         {
960           memcpy(&c->addr, a, sizeof(struct in_addr));
961           return c;
962         }
963     }
964   return NULL;
965 }
966
967 static struct conn *pid_to_conn(pid_t pid)
968 {
969   for (int i=0; i<MAX_CONNECTIONS; i++)
970     {
971       struct conn *c = &connections[i];
972       if (c->pid == pid)
973         return c;
974     }
975   return NULL;
976 }
977
978 static void put_conn(struct conn *c)
979 {
980   c->pid = 0;
981   c->tracker = NULL;
982 }
983
984 struct tracker {
985   struct in_addr addr;
986   int active_conns;
987   time_t last_access;
988   double tokens;
989 };
990
991 static struct tracker trackers[MAX_TRACKERS];
992
993 static int get_tracker(struct conn *c)
994 {
995   struct tracker *t;
996   time_t now = time(NULL);
997   int i;
998
999   for (i=0; i<MAX_TRACKERS; i++)
1000     {
1001       t = &trackers[i];
1002       if (!memcmp(&t->addr, &c->addr, sizeof(struct in_addr)))
1003         break;
1004     }
1005   if (i < MAX_TRACKERS)
1006     {
1007       if (now > t->last_access)
1008         {
1009           t->tokens += (now - t->last_access) * (double) TBF_REFILL_PER_SEC;
1010           t->last_access = now;
1011           if (t->tokens > TBF_MAX)
1012             t->tokens = TBF_MAX;
1013         }
1014       DTRACE("TBF: Using tracker %d (%.3f tokens)", i, t->tokens);
1015     }
1016   else
1017     {
1018       int min_i = -1;
1019       for (int i=0; i<MAX_TRACKERS; i++)
1020         {
1021           t = &trackers[i];
1022           if (!t->active_conns && (min_i < 0 || t->last_access < trackers[min_i].last_access))
1023             min_i = i;
1024         }
1025       if (min_i < 0)
1026         {
1027           DLOG("TBF: Out of trackers!");
1028           return 0;
1029         }
1030       t = &trackers[min_i];
1031       if (t->last_access)
1032         DTRACE("TBF: Recycling tracker %d", min_i);
1033       else
1034         DTRACE("TBF: Creating tracker %d", min_i);
1035       memset(t, 0, sizeof(*t));
1036       t->addr = c->addr;
1037       t->last_access = now;
1038       t->tokens = TBF_MAX;
1039     }
1040
1041   if (t->active_conns >= MAX_CONNS_PER_IP)
1042     {
1043       DTRACE("TBF: Too many conns per IP");
1044       return 0;
1045     }
1046
1047   if (t->tokens >= 0.999)
1048     {
1049       t->tokens -= 1;
1050       t->active_conns++;
1051       c->tracker = t;
1052       DTRACE("TBF: Passed (%d conns)", t->active_conns);
1053       return 1;
1054     }
1055   else
1056     {
1057       DTRACE("TBF: Failed");
1058       t->tokens = 0;
1059       return 0;
1060     }
1061 }
1062
1063 static void put_tracker(struct conn *c)
1064 {
1065   struct tracker *t = c->tracker;
1066   if (!t)
1067     {
1068       DLOG("put_tracker: no tracker?");
1069       sleep(5);
1070       return;
1071     }
1072   if (t->active_conns <= 0)
1073     {
1074       DLOG("put_tracker: no counter?");
1075       sleep(5);
1076       return;
1077     }
1078   t->active_conns--;
1079   DTRACE("TBF: Put tracker (%d conns remain)", t->active_conns);
1080 }
1081
1082 static void run_as_daemon(int do_fork)
1083 {
1084   int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1085   if (sk < 0)
1086     die("socket: %m");
1087
1088   int one = 1;
1089   if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
1090     die("setsockopt: %m");
1091
1092   struct sockaddr_in sa = {
1093     .sin_family = AF_INET,
1094     .sin_port = ntohs(1969),
1095     .sin_addr.s_addr = INADDR_ANY,
1096   };
1097   if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
1098     die("bind: %m");
1099   if (listen(sk, 128) < 0)
1100     die("listen: %m");
1101   // if (fcntl(sk, F_SETFL, O_NONBLOCK) < 0)
1102   //  die("fcntl: %m");
1103
1104   if (do_fork)
1105     {
1106       pid_t pid = fork();
1107       if (pid < 0)
1108         die("fork: %m");
1109       if (pid)
1110         {
1111           FILE *f = fopen(PID_FILE, "w");
1112           if (f)
1113             {
1114               fprintf(f, "%d\n", pid);
1115               fclose(f);
1116             }
1117           exit(0);
1118         }
1119
1120       chdir("/");
1121       setresgid(GID, GID, GID);
1122       setresuid(UID, UID, UID);
1123       setsid();
1124     }
1125
1126   struct sigaction sact = {
1127     .sa_handler = sigchld_handler,
1128     .sa_flags = SA_RESTART,
1129   };
1130   if (sigaction(SIGCHLD, &sact, NULL) < 0)
1131     die("sigaction: %m");
1132
1133   DLOG("Daemon ready");
1134   setproctitle("minsk: Listening");
1135   openlog("minsk", LOG_PID, LOG_LOCAL7);
1136
1137   for (;;)
1138     {
1139       struct pollfd pfd[1] = {
1140         { .fd = sk, .events = POLLIN },
1141       };
1142
1143       int nfds = poll(pfd, 1, 60000);
1144       if (nfds < 0 && errno != EINTR)
1145         {
1146           DLOG("poll: %m");
1147           sleep(5);
1148           continue;
1149         }
1150
1151       int status;
1152       pid_t pid;
1153       while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
1154         {
1155           if (!WIFEXITED(status) || WEXITSTATUS(status))
1156             DLOG("Process %d exited with strange status %x", pid, status);
1157
1158           struct conn *conn = pid_to_conn(pid);
1159           if (conn)
1160             {
1161               DTRACE("Connection with PID %d exited", pid);
1162               put_tracker(conn);
1163               put_conn(conn);
1164             }
1165           else
1166             DTRACE("PID %d exited, matching no connection", pid);
1167         }
1168
1169       if (!(pfd[0].revents & POLLIN))
1170         continue;
1171
1172       socklen_t salen = sizeof(sa);
1173       int sk2 = accept(sk, (struct sockaddr *) &sa, &salen);
1174       if (sk2 < 0)
1175         {
1176           if (errno != EINTR)
1177             {
1178               DLOG("accept: %m");
1179               sleep(5);
1180             }
1181           continue;
1182         }
1183       DTRACE("Got connection: fd=%d", sk2);
1184
1185       struct conn *conn = get_conn(&sa.sin_addr);
1186       const char *reason = NULL;
1187       if (conn)
1188         {
1189           if (!get_tracker(conn))
1190             {
1191               DLOG("Connection from %s dropped: Throttling", inet_ntoa(sa.sin_addr));
1192               put_conn(conn);
1193               conn = NULL;
1194               reason = "--- Sorry, but you are sending too many requests. Please slow down. ---\n";
1195             }
1196         }
1197       else
1198         {
1199           DLOG("Connection from %s dropped: Too many connections", inet_ntoa(sa.sin_addr));
1200           reason = "--- Sorry, maximum number of connections exceeded. Please come later. ---\n";
1201         }
1202
1203       pid = fork();
1204       if (pid < 0)
1205         {
1206           DLOG("fork failed: %m");
1207           close(sk2);
1208           continue;
1209         }
1210       if (!pid)
1211         {
1212           close(sk);
1213           if (conn)
1214             {
1215               DLOG("Accepted connection from %s", inet_ntoa(sa.sin_addr));
1216               setproctitle("minsk: %s", inet_ntoa(sa.sin_addr));
1217               child(sk2);
1218             }
1219           else
1220             {
1221               DLOG("Sending error message to %s", inet_ntoa(sa.sin_addr));
1222               setproctitle("minsk: %s ERR", inet_ntoa(sa.sin_addr));
1223               write(sk2, reason, strlen(reason));
1224             }
1225           exit(0);
1226         }
1227
1228       DTRACE("Created process %d", pid);
1229       if (conn)
1230         conn->pid = pid;
1231       close(sk2);
1232     }
1233 }
1234
1235 int main(int argc, char **argv)
1236 {
1237   if (argc > 1)
1238     {
1239       setproctitle_init(argc, argv);
1240       if (!strcmp(argv[1], "--daemon"))
1241         run_as_daemon(1);
1242       else if (!strcmp(argv[1], "--net"))
1243         run_as_daemon(0);
1244       else
1245         die("Usage: minsk [--daemon | --net]");
1246     }
1247
1248   trace = 3;
1249   parse_in();
1250   run();
1251   return 0;
1252 }