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