/* Program running ecm factoring processes in parallel, minimizing simultaneous step2. Copyright 2005 Torbjörn Granlund. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1307, USA. FIXME: We should clean up its reporting and logging functions, as well as its error handling, if we release it. */ #include /* for fork, wait4 */ #include /* for fork */ #include /* for open */ #include /* for wait4 */ #include /* for gettimeofday, wait4 */ #include /* for wait4 */ #include #include #include #include enum foo {NONE = 0, STEP1_RUNNING, STEP2_RUNNING, STEP1_DONE, STEP2_DONE}; struct jobinfo { pid_t pid; enum foo state; char filename[50]; }; #define RUNNING(x) (x.state == STEP1_RUNNING || x.state == STEP2_RUNNING) char *progname; unsigned long bush () { unsigned long ran; int fd; fd = open ("/dev/urandom", O_RDONLY); if (fd >= 0) { unsigned char buf[sizeof (long)]; size_t nread; nread = read (fd, buf, sizeof (long)); if (nread != sizeof (long)) goto stupid; ran = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; if (sizeof (long) > 4) { unsigned long ran2; ran2 = (buf[4] << 24) + (buf[5] << 16) + (buf[6] << 8) + buf[7]; ran += (ran2 << 31) << 1; } close (fd); return ran; } else { static int flag = 0; stupid: if (flag == 0) { struct timeval tp; gettimeofday (&tp, NULL); srand48 ((tp.tv_sec << 16) + tp.tv_usec + getpid ()); flag = 1; } ran = mrand48 (); } return ran; } char * pathfind (const char *command) { char *path, *p, *buf; int len, clen; clen = strlen (command); path = getenv ("PATH"); if (path == NULL) abort (); buf = malloc (strlen (path) + clen + 2); for (;;) { p = strchr (path, ':'); if (p == NULL) len = strlen (path); else len = p - path; memcpy (buf, path, len); if (buf[len - 1] != '/') { buf[len] = '/'; memcpy (buf + len + 1, command, clen + 1); } else { memcpy (buf + len, command, clen + 1); } if (access (buf, X_OK) == 0) return buf; path += len + 1; } free (buf); return NULL; } #define BUFSIZE 65536 int main (int argc, char *argv[], char *envp[]) { int nprocs, i; char *B1, *B2; struct jobinfo *jiv; pid_t pid; int wstat; char *tmpdir; char filename[50]; int (*result_chan)[2]; /* for reading output from passed */ int n_running_procs; int fd; char *ecmfactor; char buf[BUFSIZE]; char sigma[20]; int next_cofac_i; size_t nread; struct rusage rus; unsigned used_ms; nprocs = 1; /* default */ B1 = NULL; B2 = NULL; next_cofac_i = 0; progname = argv[0]; argv++; argc--; while (argc >= 2 && argv[0][0] == '-') { if (strcmp ("-B1", argv[0]) == 0) { B1 = argv[1]; argv += 2; argc -= 2; } else if (strcmp ("-B2", argv[0]) == 0) { B2 = argv[1]; argv += 2; argc -= 2; } else if (strcmp ("-n", argv[0]) == 0) { nprocs = strtoul (argv[1], 0, 0); argv += 2; argc -= 2; } else { fprintf (stderr, "%s: unknown option: %s\n", progname, argv[0]); exit (1); } } printf ("There seem to be %d cofactor files\n", argc); if (B1 == NULL) { fprintf (stderr, "%s: missing B1 value\n", progname); exit (1); } ecmfactor = pathfind ("ecmfactor"); result_chan = malloc (nprocs * sizeof (int [2])); jiv = malloc (nprocs * sizeof (struct jobinfo)); for (i = 0; i < nprocs; i++) { jiv[i].pid = 0; jiv[i].state = NONE; pipe (result_chan[i]); } tmpdir = getenv ("TMPDIR"); if (tmpdir == NULL) tmpdir = "/tmp"; n_running_procs = 0; for (;;) { int n_step2_procs = 0; for (i = 0; i < nprocs; i++) { n_step2_procs += (jiv[i].state == STEP2_RUNNING); } for (i = 0; i < nprocs; i++) { if (! RUNNING (jiv[i])) { if (jiv[i].state == STEP1_DONE) { if (n_step2_procs * 2 >= nprocs) continue; fprintf (stderr, "STARTING NEW STEP 2 JOB\n"); pid = fork (); if (pid == 0) { /* Child */ char *outv[6], **op = outv; dup2 (result_chan[i][1], 1); close (result_chan[i][0]); close (result_chan[i][1]); *op++ = "ecmfactor"; *op++ = "-resume"; *op++ = jiv[i].filename; *op++ = B1; if (B2 != NULL) *op++ = B2; *op = NULL; execve (ecmfactor, outv, envp); fprintf (stderr, "cannot execute %s\n", ecmfactor); abort (); } n_running_procs++; jiv[i].pid = pid; jiv[i].state = STEP2_RUNNING; continue; } for (;;) { if (next_cofac_i == 0) sprintf (sigma, "%lu", bush ()); strcpy (filename, argv[next_cofac_i]); next_cofac_i = (next_cofac_i + 1) % argc; fd = open (filename, O_RDONLY); if (fd != -1) break; usleep (50000); } sprintf (jiv[i].filename, "%s/ecm-save-%u", tmpdir, i); unlink (jiv[i].filename); fprintf (stderr, "STARTING NEW STEP 1 JOB\n"); pid = fork (); if (pid == 0) { /* Child */ char *outv[8], **op = outv; dup2 (result_chan[i][1], 1); close (result_chan[i][0]); close (result_chan[i][1]); dup2 (fd, 0); close (fd); *op++ = "ecmfactor"; *op++ = "-save"; *op++ = jiv[i].filename; *op++ = "-sigma"; *op++ = sigma; *op++ = B1; *op++ = "1"; *op = NULL; execve (ecmfactor, outv, envp); fprintf (stderr, "cannot execute %s\n", ecmfactor); abort (); } close (fd); n_running_procs++; jiv[i].pid = pid; jiv[i].state = STEP1_RUNNING; continue; } } fprintf (stderr, "ABOUT TO WAIT (%d jobs running)\n", n_running_procs); pid = wait4 (0, &wstat, 0, &rus); if (pid == -1) { fprintf (stderr, "wait returned error %d\n", errno); abort (); } n_running_procs--; used_ms = rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000; if (WIFSIGNALED (wstat)) { fprintf (stderr, "*** child got signal %d\n", WTERMSIG (wstat)); exit (1); } for (i = 0; i < nprocs; i++) { if (jiv[i].pid == pid) goto yee; } abort (); yee: if (jiv[i].state == STEP1_RUNNING) { nread = read (result_chan[i][0], buf, BUFSIZE); jiv[i].state = STEP1_DONE; fprintf (stderr, "STEP 1 JOB %d FINISHED (used %u ms)\n", i, used_ms); } else if (jiv[i].state == STEP2_RUNNING) { unlink (jiv[i].filename); nread = read (result_chan[i][0], buf, BUFSIZE); jiv[i].state = STEP2_DONE; fprintf (stderr, "STEP 2 JOB %d FINISHED (used %u ms)\n", i, used_ms); } else abort (); if (WEXITSTATUS (wstat) == 0) { FILE *fs; fs = popen ("mail -s \"NEW FACTOR\" invalid@example.net", "w"); fwrite (buf, nread, 1, fs); pclose (fs); } } } /* To perform only step 1: $ ./ecm -save toto B1 1 < composite Then to perform step 2: $ ./ecm -resume toto B1 [B2] */