1
/* Copyright (c) 2003 SuSE Linux AG
3
* This program is free software; you can redistribute it and/or modify
4
* it under the terms of the GNU General Public License as published by
5
* the Free Software Foundation; either version 2, or (at your option)
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program (see the file COPYING); if not, write to the
15
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18
****************************************************************
27
#include <sys/types.h>
29
#include <sys/select.h>
31
#include <sys/ioctl.h>
32
#include <sys/socket.h>
34
#include <sys/sysinfo.h>
48
#define timerdiff(n,l) (__extension__ ({ (((n).tv_sec-(l).tv_sec)*1000)+(((n).tv_usec-(l).tv_usec)/1000); }))
50
typedef enum _boolean {false, true} boolean;
54
static long int numcpu = -1;
56
static struct termios tio;
57
static struct winsize wz;
62
static sig_atomic_t wzok;
64
static boolean isstart;
65
static struct sigaction sa;
66
static struct timeval glastio;
67
static struct timeval now;
68
static struct timeval lastlim;
69
static char *run_mode = NULL;
70
static struct makenode **nodevec;
72
static enum { Unknown, Preload, NoPreload } ispreload = Unknown;
74
#define PBUF_SIZE 8192
81
struct timeval lastio;
87
static struct prg *prgs;
88
static int inpar, par;
89
static int pidpipe[2];
90
static double iorate = 800.0;
92
void sighandler_nopreload(int x)
95
ispreload = NoPreload;
99
void sighandler_preload(int x)
105
void *xcalloc(size_t nmemb, size_t size)
108
if ((r = (void *)calloc(nmemb, size)) == 0)
110
fprintf(stderr, "calloc: out of memory\n");
116
static int splashpos = -1;
117
static char *splashcfg;
119
int calcsplash(int x, int n, char *opt)
127
for (i = 0; i <= x; i++)
129
if ((p = strchr(opt, ':')) == 0)
156
waitpid(splashpid, &status, 0);
157
} while (errno == EINTR);
167
for (s = 0; s < par; s++)
174
void callsplash(int n, const char *path, char *action)
182
if (n < 0 || splashpos < 0)
184
if (splashpos + n > 65535)
185
n = 65535 - splashpos;
187
if (stat("/proc/splash", &stb))
189
p = strrchr(path, '/');
192
for (p = path; *p; p++)
193
if ((*p == 'S' || *p == 'K') && p[1] >= '0' && p[1] <= '9' && p[2] >= '0' && p[2] <= '9' && p[3])
201
if (strlen(p) + strlen(action) + 2 > sizeof(tbuf))
203
sprintf(tbuf, "%s%s%s", p, *action ? " " : "", action);
204
sprintf(sbuf, "%d:%d", splashpos - n, n);
207
if (pid == (pid_t)-1)
214
while (dup2(2, 1) < 0 && (errno == EINTR))
217
execl("/sbin/splash", "splash", "-p", sbuf, "-t", tbuf, splashcfg, (char *)0);
221
void writebuf(struct prg *p)
228
r = write(2, b, p->len);
240
static int checksystem(const int par, const boolean start, const boolean limit)
242
const int pg_size = sysconf(_SC_PAGESIZE);
243
const long int minphys_bytes = (sysconf(_SC_LONG_BIT) > 32L) ? (2<<22) : (2<<21);
244
const long int avphys_pg = sysconf(_SC_AVPHYS_PAGES);
246
unsigned long int prcs_run, prcs_blked;
256
minphys_pg = avphys_pg;
258
minphys_pg = minphys_bytes / pg_size;
260
if (avphys_pg < minphys_pg)
269
if (read_proc(&prcs_run, &prcs_blked))
272
/* if we have preload running, we expect I/O not to be a problem */
273
if (ispreload != NoPreload)
276
newpar = (par*numcpu) - prcs_run + 1; /* +1 for startpar its self */
277
newpar -= (int)(((double)prcs_blked)*iorate); /* I/O load reduction */
280
fprintf(stderr, "checksystem par=%d newpar=%d (prcs_run=%lu) %ld\n", par, newpar, prcs_run, time(0));
289
static inline int checklimit(const int par, const boolean start)
291
return checksystem(par, start, true);
294
static inline int checkpar(const int par, const boolean start)
296
return checksystem(par, start, false);
299
#define SOCK_PATH "/dev/shm/preload_sock"
302
static int checkdevpts(void)
304
/* /dev/pts is always mounted */
309
* Based on __posix_openpt() from glibc. Reimplemented here to work
310
* around the problem with getpt() failing for the entire process life
311
* time if /dev/pts/ is missing the first time it is called but
312
* mounted while the process is running. BSD style pts is not
313
* supported, but might be copied from glibc too if there is need.
315
#define DEVFS_SUPER_MAGIC 0x1373
316
#define DEVPTS_SUPER_MAGIC 0x1cd1
318
static int startpar_getpt(void) {
319
int fd = open("/dev/ptmx", O_RDWR|O_NOCTTY);
325
/* Check that the /dev/pts filesystem is mounted
326
or if /dev is a devfs filesystem (this implies /dev/pts). */
327
if ((statfs ("/dev/pts", &fsbuf) == 0
328
&& fsbuf.f_type == DEVPTS_SUPER_MAGIC)
329
|| (statfs ("/dev", &fsbuf) == 0
330
&& fsbuf.f_type == DEVFS_SUPER_MAGIC))
332
/* Everything is ok, switch to the getpt() in libc. */
336
/* If /dev/pts is not mounted then the UNIX98 pseudo terminals
344
static int checkdevpts(void)
346
int ptsfd = startpar_getpt();
352
else if (ptsname(ptsfd) == 0 || grantpt(ptsfd) || unlockpt(ptsfd))
365
void run(struct prg *p)
368
pid_t parent = getpid();
377
fprintf(stderr, "could not get pty for %s\n", p->name);
379
else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))
381
fprintf(stderr, "could not init pty for %s\n", p->name);
385
if ((p->pid = fork()) == (pid_t)-1)
388
fprintf(stderr, "could not fork %s\n", p->name);
400
(void)signal(SIGINT, SIG_DFL);
401
(void)signal(SIGQUIT, SIG_DFL);
402
(void)signal(SIGSEGV, SIG_DFL);
403
(void)signal(SIGTERM, SIG_DFL);
404
(void)signal(SIGCHLD, SIG_DFL);
411
while (close(1) < 0 && (errno == EINTR))
413
if (open(m, O_RDWR) != 1)
418
while (dup2(1, 2) < 0 && (errno == EINTR))
420
tio.c_oflag &= ~OPOST;
421
if (tcsetattr(1, TCSANOW, &tio))
424
ioctl(1, TIOCSWINSZ, &wz);
430
while (dup2(2, 1) < 0 && (errno == EINTR))
436
if (!strcmp(arg, "start"))
440
struct sockaddr_un remote;
443
s = socket(AF_UNIX, SOCK_STREAM, 0);
446
memset(&remote, 0, sizeof(struct sockaddr_un));
447
remote.sun_family = AF_UNIX;
448
strcpy(remote.sun_path, SOCK_PATH);
449
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
451
t = connect(s, (struct sockaddr *)&remote, len);
454
if (ispreload != Preload)
455
kill(parent, SIGUSR1);
456
send(s, p->name, strlen(p->name), 0);
457
recv(s, str, 100, 0);
459
else if ( ispreload == Unknown)
462
* if we connected to preload once, we know it ran.
463
* In case we can't connect to it later, it means it did
464
* its job and we can guess I/O is no longer a problem.
466
kill(parent, SIGUSR2);
470
* if we use preload, we fork again to make bootcharts easier to read.
471
* The reason is that the name of the init script will otherwise be used
472
* when in reality the above code waited for preload. If we fork away
473
* before the exec, the waiting code will be folded into startpar
478
int ret = waitpid(child, &status, 0);
481
exit(WEXITSTATUS(status));
489
snprintf(path, sizeof(path), "/etc/init.d/%s", p->name);
490
execlp(path, p->arg0, arg, (char *)0);
493
execlp(p->name, p->arg0, arg, (char *)0);
495
execlp(p->name, p->arg0, (char *)0);
500
int run_single(const char *prg, const char *arg0, int spl)
505
if ((pid = fork()) == (pid_t)-1)
508
fprintf(stderr, "could not fork %s\n", prg);
514
(void)signal(SIGINT, SIG_DFL);
515
(void)signal(SIGQUIT, SIG_DFL);
516
(void)signal(SIGSEGV, SIG_DFL);
517
(void)signal(SIGTERM, SIG_DFL);
518
(void)signal(SIGCHLD, SIG_DFL);
520
while (dup2(2, 1) < 0 && (errno == EINTR))
526
snprintf(path, sizeof(path), "/etc/init.d/%s", prg);
527
execlp(path, arg0 ? arg0 : path, arg, (char *)0);
530
execlp(prg, arg0 ? arg0 : prg, arg, (char *)0);
532
execlp(prg, arg0 ? arg0 : prg, (char *)0);
537
while ((waitpid(pid, &r, 0) == (pid_t)-1) && (errno == EINTR))
539
callsplash(spl, prg, arg);
540
return WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
543
void do_forward(void)
548
while ((r = read(0, buf, sizeof(buf))) > 0)
565
static char *gtimo_buf;
566
static size_t gtimo_bufsize;
567
static size_t gtimo_buflen;
569
void storebuf(struct prg *p)
571
if ((gtimo_buflen + p->len) > gtimo_bufsize)
573
writebuf(p); /* In case of overflow or memory shortage */
577
(void)memcpy(gtimo_buf + gtimo_buflen, p->buf, p->len);
578
gtimo_buflen += p->len;
585
size_t len = gtimo_buflen;
586
char * buf = gtimo_buf;
589
return; /* In case of memory shortage */
593
int r = write(2, buf, len);
609
void detach(struct prg *p, const int store)
612
int flags = fcntl(p->fd, F_GETFL);
619
fcntl(p->fd, F_SETFL, flags);
620
while ((r = read(p->fd, p->buf, sizeof(p->buf))) > 0)
629
fcntl(p->fd, F_SETFL, flags);
630
if (r == -1 && errno == EWOULDBLOCK)
632
if ((r = fork()) == 0)
634
while (dup2(p->fd, 0) < 0 && (errno == EINTR))
636
while (dup2(2, 1) < 0 && (errno == EINTR))
639
execlp(myname, myname, "-f", "--", p->name, NULL);
649
static void sigchld(int sig __attribute__ ((unused)))
652
write(pidpipe[1], &c, 1);
655
static void sigwinch(int sig __attribute__ ((unused)))
657
if (ioctl(0, TIOCGWINSZ, &wz) < 0)
662
if (wz.ws_row == 0) wz.ws_row = 24;
663
if (wz.ws_col == 0) wz.ws_col = 80;
664
snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d", wz.ws_row);
665
snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
668
void usage(int status)
670
fprintf(stderr, "usage: startpar [options] [-a arg] prgs\n");
671
fprintf(stderr, " run given programs parallel\n");
672
fprintf(stderr, " startpar [options] [-P prev] [-R run] [-S <start>:<num>] -M mode\n");
673
fprintf(stderr, " run parallel with Makefile\n");
674
fprintf(stderr, " startpar -v\n");
675
fprintf(stderr, " show version number\n");
676
fprintf(stderr, "general options:\n");
677
fprintf(stderr, " -p parallel tasks\n");
678
fprintf(stderr, " -t I/O timeout\n");
679
fprintf(stderr, " -T global I/O timeout\n");
683
int main(int argc, char **argv)
696
struct prg *gtimo_running = 0;
697
struct prg *interactive_task = NULL;
698
char *prev_level = getenv("PREVLEVEL");
699
char *run_level = getenv("RUNLEVEL");
702
(void)signal(SIGUSR1, sighandler_preload);
703
(void)signal(SIGUSR2, sighandler_nopreload);
705
(void)signal(SIGCHLD, SIG_DFL);
706
numcpu = sysconf(_SC_NPROCESSORS_ONLN);
709
while ((c = getopt(argc, argv, "fhp:t:T:a:M:P:R:S:vi:")) != EOF)
720
gtimo = atoi(optarg);
741
printf("startpar version %s\n", VERSION);
747
iorate = atof(optarg);
763
char *so = strchr(splashopt, ':');
768
splashpos = atoi(splashopt);
771
splashcfg = getenv("SPLASHCFG");
781
if (!strcmp(run_mode, "boot"))
783
else if (!strcmp(run_mode, "halt"))
785
else if (!strcmp(run_mode, "start") || !strcmp(run_mode, "stop"))
788
if (!prev_level || !run_level)
790
fprintf(stderr, "You must specify previous and next runlevels\n");
796
fprintf(stderr, "invalid run mode %s\n", run_mode);
799
snprintf(makefile, sizeof(makefile), "/etc/init.d/.depend.%s", run_mode);
800
parse_makefile(makefile);
801
check_run_files(run_mode, prev_level, run_level);
803
argc = tree_entries; /* number of handled scripts */
804
isstart = !strcmp(arg, "start");
811
if (par > argc) /* not more than the number of all scripts */
814
inpar = par; /* the original argument of parallel procs per cpu */
816
par = checkpar(inpar, isstart); /* the number of parallel procs on all cpu's */
818
if (par > argc) /* not more than the number of all scripts */
821
nodevec = xcalloc(argc, sizeof(*nodevec));
829
isstart = !strcmp(arg, "start");
836
if (par > argc) /* not more than the number of all scripts */
839
inpar = par; /* the original argument of parallel procs per cpu */
841
par = checkpar(inpar, isstart); /* the number of parallel procs on all cpu's */
843
if (par > argc) /* not more than the number of all scripts */
848
resvec = (int *)xcalloc(argc, sizeof(int));
849
for (i = 0; i < argc; i++)
856
if ((*nodevec = pickup_task()))
858
*resvec = run_single((*nodevec)->name, (*nodevec)->arg0, calcsplash(0, 1, splashopt));
859
finish_task(*nodevec);
862
*resvec = run_single(*argv, *argv, calcsplash(0, 1, splashopt));
866
prgs = (struct prg *)xcalloc(par, sizeof *prgs);
867
gtimo_bufsize = par * PBUF_SIZE;
868
gtimo_buf = (char *) calloc(gtimo_bufsize, sizeof(char));
870
gtimo_bufsize = 0; /* Accept error due memory shortage */
872
sa.sa_handler = sigwinch;
873
sa.sa_flags = SA_RESTART|SA_NODEFER;
874
(void)sigemptyset(&sa.sa_mask);
875
if (sigaction(SIGWINCH, &sa, 0))
877
perror("sigwinch sigaction");
881
if (tcgetattr(0, &tio))
886
if (ioctl(0, TIOCGWINSZ, &wz) == 0)
888
if (wz.ws_row == 0) wz.ws_row = 24;
889
if (wz.ws_col == 0) wz.ws_col = 80;
891
strcat(&sz.env_row[0], "LINES=");
892
strcat(&sz.env_col[0], "COLUMNS=");
893
snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d", wz.ws_row);
894
snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
901
fcntl(pidpipe[0], F_SETFL, FNONBLOCK);
902
fcntl(pidpipe[1], F_SETFL, FNONBLOCK);
903
sa.sa_handler = sigchld;
904
sa.sa_flags = SA_RESTART;
905
(void)sigemptyset(&sa.sa_mask);
906
if (sigaction(SIGCHLD, &sa, 0))
908
perror("sigchld sigaction");
912
gettimeofday(&glastio, 0);
913
limit = checklimit(inpar, isstart);
925
gettimeofday(&now, 0);
929
if ((diff = timerdiff(now, lastlim)) >= 300 || diff < 0)
932
fprintf(stderr, "%d: doing checklimit after %ldms %ld\n", getpid(), diff, time(0));
934
if ((limit = checklimit(inpar, isstart)) > argc)
935
limit = argc; /* not more than the number of all scripts */
940
fprintf(stderr, "par=%d, inpar=%d, limit=%d (diff=%ld)\n", par, inpar, limit, diff);
942
for (s = 0; s < par; s++) /* never leave this with break!! */
944
account: /* for the new process below */
946
devpts = checkdevpts();
948
if (p == interactive_task)
949
continue; /* don't count this here */
951
active++; /* count all running procs */
954
if (interactive_task)
955
continue; /* dont't start new processes */
957
continue; /* nothing to do */
961
continue; /* load balancing */
964
if ((nodevec[num] = pickup_task()) == NULL)
966
if (nodevec[num]->interactive)
967
interactive_task = p;
968
p->name = nodevec[num]->name;
969
p->arg0 = nodevec[num]->arg0 ? nodevec[num]->arg0 : nodevec[num]->name;
975
p->splashadd = calcsplash(num, argc, splashopt);
977
if (interactive_task)
978
continue; /* don't start this here */
981
interactive_task = p; /* no /dev/pts, treat as interactive */
989
finish_task(nodevec[p->num]);
991
gettimeofday(&now, 0);
993
goto account; /* take the new process into account */
997
FD_SET(p->fd, &rset);
1002
if ((last < 0) || timercmp(&tv,&p->lastio,>))
1007
} /* for (s = 0; s < par; s++) */
1009
if (interactive_task)
1013
p = interactive_task;
1014
resvec[p->num] = run_single(p->name, p->arg0, p->splashadd);
1016
finish_task(nodevec[p->num]);
1019
interactive_task = NULL;
1027
fprintf(stderr, "ERROR: not all processed (%d of %d)\n", num, argc);
1029
if ((pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) > 0)
1030
fprintf(stderr, "ERROR: not all processes are checked\n");
1035
fprintf(stderr, "active = %d\n", active);
1037
if (active == 1 && last >= 0)
1040
if ((pid = waitpid(p->pid, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) == 0)
1047
FD_SET(pidpipe[0], &rset);
1048
/* drain the pidpipe */
1049
while ((c = read(pidpipe[0], pipebuf, sizeof pipebuf)) > 0)
1053
pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED);
1057
if (pid == splashpid)
1058
splashpid = (pid_t)0;
1059
for (s = 0; s < par; s++)
1066
if (WSTOPSIG(r) == SIGTTIN)
1068
pid_t pg = getpgid(pid);
1070
killpg(pg, SIGCONT);
1074
callsplash(p->splashadd, p->name, arg);
1075
resvec[p->num] = WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
1077
finish_task(nodevec[p->num]);
1079
if (gtimo_running == p)
1083
detach(p, GTIMO_OFFL);
1087
else if (gtimo_running)
1091
detach(p, GTIMO_USED);
1097
detach(p, GTIMO_OFFL);
1101
} /* for (s = 0; s < par; s++) */
1106
continue; /* start new processes? */
1112
if (gtimo >= 0 && !gtimo_running && last >= 0 && prgs[last].pid)
1114
struct timeval gl = glastio;
1116
if ((timo < 0) || timercmp(&tv,&gl,>))
1125
if (timo >= 0 || isgtimo)
1127
int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1128
struct timeval wait;
1130
timersub(&tv, &now, &wait);
1131
if (wait.tv_usec < 0)
1133
wait.tv_usec += 1000000;
1136
if (wait.tv_sec >= 0)
1138
int check = limit < par && num < argc;
1140
if (check) /* shorten timeout for new limit and procs ... */
1143
wait.tv_usec = (300 - diff) * 1000;
1146
fprintf(stderr, "going into select1 %d %ld %ld\n", last, wait.tv_sec, wait.tv_usec);
1148
r = select(setfd + 1, &rset, 0, 0, (last >= 0 || check) ? &wait : 0);
1150
if (check && (r == 0)) /* ... but do not throw out messages to early!!! */
1155
wait.tv_sec = 0; /* Avoid looping around (does this ever happen?) */
1156
wait.tv_usec = 20*1000;
1157
r = select(setfd + 1, &rset, 0, 0, last >= 0 ? &wait : 0);
1162
int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1163
r = select(setfd + 1, &rset, 0, 0, 0);
1175
if (last < 0) /* just in case... */
1179
if (isgtimo && p->pid)
1184
for (s = 0; s < par; s++)
1189
if (!FD_ISSET(p->fd, &rset))
1191
r = read(p->fd, p->buf + p->len, sizeof(p->buf) - p->len);
1194
if (!gtimo_running || p == gtimo_running)
1201
if (p->len == sizeof(p->buf))
1203
for (i = p->len - 1; i >= 0; i--)
1205
if (p->buf[i] == '\n')
1212
p->len = i; /* writebuf clears p->len */
1213
if (p->len < sizeof(p->buf))
1214
memmove(p->buf, p->buf + p->len, sizeof(p->buf) - p->len);
1215
p->len = sizeof(p->buf) - p->len;
1218
} /* for (s = 0; s < par; s++) */
1225
print_run_result(resvec, nodevec, run_mode);
1228
for (i = 0; i < argc; i++)
1231
if (resvec[i] == 255)
1233
fprintf(stderr, "ERROR: forgotten process??\n");
1238
printf(i ? " %d" : "%d", resvec[i]);
1239
#endif /* VERBOSE */
1243
#endif /* VERBOSE */