Add to git.
[dlife.git] / main.c
1 /* DLIFE Copyright (C) 2000 Richard W.M. Jones <rich@annexia.org>
2  * and other authors listed in the ``AUTHORS'' file.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * $Id: main.c,v 1.3 2002/12/11 17:16:21 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29
30 #ifdef HAVE_STRING_H
31 #include <string.h>
32 #endif
33
34 #ifdef HAVE_PWD_H
35 #include <pwd.h>
36 #endif
37
38 #ifdef HAVE_GRP_H
39 #include <grp.h>
40 #endif
41
42 #ifdef HAVE_SYS_TYPES_H
43 #include <sys/types.h>
44 #endif
45
46 #ifdef HAVE_SYSLOG_H
47 #include <syslog.h>
48 #endif
49
50 #include "cell.h"
51 #include "soup.h"
52 #include "load.h"
53 #include "params.h"
54 #include "random.h"
55 #include "state.h"
56 #include "image.h"
57
58 unsigned soup_fetch_failure          = 1000000;
59 unsigned inc_pc_failure              = 1000000;
60 unsigned insn_exec_failure           = 1000000;
61 unsigned access_control_failure      =      10;
62 unsigned cell_initialization_failure = 1000000;
63 int reaper_invoke_threshold          = 80;
64 int reaper_reap_threshold            = 79;
65 int save_period                      = 5;
66 int outgoing_period                  = 10;
67 int incoming_period                  = 60;
68 int info_period                      = 60;
69 int max_cells_incoming_per_pass      = 4;
70
71 int verbose = 0;
72
73 static int soup_size = 128*1024; /* Read from configuration file. */
74
75 static const char *conf_file = CONFDIR "/soup.conf";
76 static const char *save_dir = SPOOLDIR;
77
78 static void parse_args (int argc, char *argv[]);
79 static void read_config_file (void);
80 static int detect_nr_cpus (void);
81
82 int
83 main (int argc, char *argv[])
84 {
85   char filename[256];
86   int nr_cpus, i;
87
88   /* Parse the command line. */
89   parse_args (argc, argv);
90
91   /* Read the configuration file. */
92   read_config_file ();
93
94   /* Initialize stuff. */
95   random_init ();
96
97 #ifdef HAVE_OPENLOG
98   /* Open connection to syslog. */
99   openlog ("dlife_soup", LOG_PID, LOG_DAEMON);
100 #endif
101
102   /* Find out how many processors are available. */
103   nr_cpus = detect_nr_cpus ();
104
105   /* Find existing saves directory. If there is a soup image in there,
106    * then we will load it. Otherwise we will start with the god cell.
107    */
108   if (chdir (save_dir) < 0)
109     {
110       perror (save_dir);
111       exit (1);
112     }
113
114 #ifdef HAVE_NICE
115   /* Reduce our priority down to the minimum. */
116   nice (64);
117 #endif
118
119 #if defined(HAVE_GETUID) && defined(HAVE_SETUID) && defined(HAVE_SETGID) && defined(HAVE_GETPWNAM) && defined(HAVE_INITGROUPS)
120   /* If we are running as root, change to user dlife. */
121   if (getuid () == 0)
122     {
123       struct passwd *pw = getpwnam ("dlife");
124
125       if (pw)
126         {
127           initgroups ("dlife", pw->pw_gid);
128           setgid (pw->pw_gid);
129           setuid (pw->pw_uid);
130         }
131       else
132         {
133           fprintf (stderr, "cannot change to user/group dlife");
134         }
135     }
136 #endif
137
138 #ifdef HAVE_SYSLOG
139   syslog (LOG_INFO,
140           "version " VERSION " starting [conf="
141           CONFDIR ", spool=" SPOOLDIR ", cpus=%d]", nr_cpus);
142 #endif
143
144   /* Fork once for each processor. */
145   for (i = 0; i < nr_cpus; ++i)
146     {
147       int pid;
148
149       pid = fork ();
150       if (pid < 0)
151         {
152           perror ("fork");
153           abort ();
154         }
155
156       if (pid == 0)
157         {
158           /* This is the child process. */
159           struct state *cpu_state;
160
161           /* Try to load the previous soup image. If the soup image
162            * cannot be found, then instead load the god cell.
163            */
164           sprintf (filename, "saved/soup%d.img", i);
165
166           cpu_state = image_load (filename, soup_size, i);
167
168           if (cpu_state)
169             {
170               if (verbose)
171                 printf ("CPU %d: starting from previous image file.\n", i);
172             }
173           else
174             {
175               struct cell *god;
176
177               if (verbose)
178                 printf ("CPU %d: no soup image found, loading god cell\n", i);
179
180               cpu_state = state_malloc (soup_size, i);
181
182               if ((god = load_cell (cpu_state, "god.dlo")) == 0)
183                 {
184                   fprintf (stderr, "dlife_soup: cannot load god cell god.dlo\n");
185 #ifdef HAVE_SYSLOG
186                   syslog (LOG_ERR,
187                           "cannot load god cell " SPOOLDIR
188                           "/god.dlo -- exiting");
189 #endif
190                   abort ();
191                 }
192               cell_activate (cpu_state, god);
193             }
194
195           cpu_state->filename = strdup (filename);
196
197           /* Detach from the terminal. */
198           close (0);
199           close (1);
200           close (2);
201           setsid ();
202
203           /* Start running. */
204           run_thread (cpu_state);
205
206           exit (0);
207         }
208     }
209
210   /* Parent process exits. The children will automatically be
211    * cleaned up by init(8).
212    */
213   exit (0);
214 }
215
216 /* Parse the command line arguments. */
217 static void
218 parse_args (int argc, char *argv[])
219 {
220   int c;
221
222   while ((c = getopt (argc, argv, "f:r:v")) != -1)
223     {
224       switch (c)
225         {
226         case 'f':
227           conf_file = optarg;
228           break;
229         case 'r':
230           save_dir = optarg;
231           break;
232         case 'v':
233           verbose = 1;
234           break;
235         default:
236           fprintf (stderr,
237                "usage: dlife_soup [-f conf-file] [-r save-dir] [-v]\n");
238           exit (1);
239         }
240     }
241 }
242
243 /* Return true if n is a power of 2. */
244 static int
245 is_power_2 (int n)
246 {
247   while ((n & 1) == 0)
248     n >>= 1;
249   return n == 1;
250 }
251
252 static void
253 read_config_file (void)
254 {
255   FILE *fp;
256   char buffer[256], *t;
257
258   fp = fopen (conf_file, "r");
259   if (fp == 0)
260     {
261       if (verbose) printf ("No configuration file found.\n");
262       return;
263     }
264
265   while (fgets (buffer, sizeof buffer, fp) != 0)
266     {
267       /* Remove trailing \n and \r. */
268       t = buffer + strlen (buffer) - 1;
269       while (t >= buffer && (*t == '\n' || *t == '\r'))
270         *t-- = '\0';
271
272       /* Remove any comments (after a ``#'' character). */
273       t = strchr (buffer, '#');
274       if (t)
275         *t = '\0';
276
277       /* Look for lines which we understand. */
278       if (sscanf (buffer, "soup_fetch_failure %u", &soup_fetch_failure) == 1)
279         {
280           /* No checks required. */
281           continue;
282         }
283       if (sscanf (buffer, "inc_pc_failure %u", &inc_pc_failure) == 1)
284         {
285           /* No checks required. */
286           continue;
287         }
288       if (sscanf (buffer, "insn_exec_failure %u", &insn_exec_failure) == 1)
289         {
290           /* No checks required. */
291           continue;
292         }
293       if (sscanf (buffer, "access_control_failure %u", &access_control_failure) == 1)
294         {
295           /* No checks required. */
296           continue;
297         }
298       if (sscanf (buffer, "cell_initialization_failure %u", &cell_initialization_failure) == 1)
299         {
300           /* No checks required. */
301           continue;
302         }
303       if (sscanf (buffer, "reaper_invoke_threshold %d", &reaper_invoke_threshold) == 1)
304         {
305           if (reaper_invoke_threshold < 0 || reaper_invoke_threshold > 100)
306             {
307               fprintf (stderr, "reaper_invoke_threshold is a percentage: it must be 0 - 100\n");
308               exit (1);
309             }
310           continue;
311         }
312       if (sscanf (buffer, "reaper_reap_threshold %d", &reaper_reap_threshold) == 1)
313         {
314           if (reaper_reap_threshold < 0 || reaper_reap_threshold > 100)
315             {
316               fprintf (stderr, "reaper_reap_threshold is a percentage: it must be 0 - 100\n");
317               exit (1);
318             }
319           continue;
320         }
321       if (sscanf (buffer, "soup_size %d", &soup_size) == 1)
322         {
323           if (soup_size <= 0 || ! is_power_2 (soup_size))
324             {
325               fprintf (stderr, "soup_size must be a power of 2\n");
326               exit (1);
327             }
328           continue;
329         }
330       if (sscanf (buffer, "save_period %d", &save_period) == 1)
331         {
332           /* No checks required. */
333           continue;
334         }
335       if (sscanf (buffer, "outgoing_period %d", &outgoing_period) == 1)
336         {
337           /* No checks required. */
338           continue;
339         }
340       if (sscanf (buffer, "incoming_period %d", &incoming_period) == 1)
341         {
342           /* No checks required. */
343           continue;
344         }
345       if (sscanf (buffer, "info_period %d", &info_period) == 1)
346         {
347           /* No checks required. */
348           continue;
349         }
350     }
351 }
352
353 /* Detect the number of CPUs available. This is very operating system
354  * specific.
355  */
356
357 #ifdef linux
358
359 #ifdef __alpha__
360
361 static int
362 detect_nr_cpus ()
363 {
364   FILE *fp;
365   int n = -1;
366   char line[256];
367
368   fp = fopen ("/proc/cpuinfo", "r");
369   if (fp == 0)
370     {
371       perror ("/proc/cpuinfo");
372       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
373       return 1;
374     }
375
376   while (fgets (line, sizeof line, fp))
377     {
378       if (sscanf (line, "cpus detected           : %d", &n) == 1)
379         break;
380     }
381
382   fclose (fp);
383
384   if (verbose)
385     printf ("number of CPUs detected: %d\n", n);
386
387   if (n <= 0)
388     {
389       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
390       return 1;
391     }
392
393   return n;
394 }
395
396 #elif __sparc__
397
398 static int
399 detect_nr_cpus ()
400 {
401   FILE *fp;
402   int n = -1;
403   char line[256];
404
405   fp = fopen ("/proc/cpuinfo", "r");
406   if (fp == 0)
407     {
408       perror ("/proc/cpuinfo");
409       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
410       return 1;
411     }
412
413   while (fgets (line, sizeof line, fp))
414     {
415       if (sscanf (line, "ncpus active            : %d", &n) == 1)
416         break;
417     }
418
419   fclose (fp);
420
421   if (verbose)
422     printf ("number of CPUs detected: %d\n", n);
423
424   if (n <= 0)
425     {
426       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
427       return 1;
428     }
429
430   return n;
431 }
432
433 #else /* !__alpha__ || __sparc__ */
434
435 static int
436 detect_nr_cpus ()
437 {
438   FILE *fp;
439   int n, c = 0;
440   char line[256];
441
442   fp = fopen ("/proc/cpuinfo", "r");
443   if (fp == 0)
444     {
445       perror ("/proc/cpuinfo");
446       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
447       return 1;
448     }
449
450   while (fgets (line, sizeof line, fp))
451     {
452       if (sscanf (line, "processor : %d", &n) == 1 && n+1 > c)
453         {
454           c = n+1;
455         }
456     }
457
458   fclose (fp);
459
460   if (verbose)
461     printf ("number of CPUs detected: %d\n", c);
462
463   if (c <= 0)
464     {
465       fprintf (stderr, "warning: cannot detect number of CPUs available\n");
466       return 1;
467     }
468
469   return c;
470 }
471
472 #endif /* !__alpha__ */
473
474 #else /* !linux */
475
476 static void
477 detect_nr_cpus ()
478 {
479   fprintf (stderr, "warning: cannot detect number of CPUs available on this platform\n");
480   return 1;
481 }
482
483 #endif