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>
45
#define timerdiff(n,l) (__extension__ ({ (((n).tv_sec-(l).tv_sec)*1000)+(((n).tv_usec-(l).tv_usec)/1000); }))
47
typedef enum _boolean {false, true} boolean;
51
static long int numcpu = -1;
53
static struct termios tio;
54
static struct winsize wz;
59
static sig_atomic_t wzok;
61
static boolean isstart;
62
static struct sigaction sa;
63
static struct timeval glastio;
64
static struct timeval now;
65
static struct timeval lastlim;
66
static char *run_mode = NULL;
67
static struct makenode **nodevec;
69
static enum { Unknown, Preload, NoPreload } ispreload = Unknown;
71
#define PBUF_SIZE 8192
78
struct timeval lastio;
84
static struct prg *prgs;
85
static int inpar, par;
86
static int pidpipe[2];
87
static double iorate = 800.0;
89
void sighandler_nopreload(int x)
92
ispreload = NoPreload;
96
void sighandler_preload(int x)
102
void *xcalloc(size_t nmemb, size_t size)
105
if ((r = (void *)calloc(nmemb, size)) == 0)
107
fprintf(stderr, "calloc: out of memory\n");
113
static int splashpos = -1;
114
static char *splashcfg;
116
int calcsplash(int x, int n, char *opt)
124
for (i = 0; i <= x; i++)
126
if ((p = strchr(opt, ':')) == 0)
153
waitpid(splashpid, &status, 0);
154
} while (errno == EINTR);
164
for (s = 0; s < par; s++)
171
void callsplash(int n, const char *path, char *action)
179
if (n < 0 || splashpos < 0)
181
if (splashpos + n > 65535)
182
n = 65535 - splashpos;
184
if (stat("/proc/splash", &stb))
186
p = strrchr(path, '/');
189
for (p = path; *p; p++)
190
if ((*p == 'S' || *p == 'K') && p[1] >= '0' && p[1] <= '9' && p[2] >= '0' && p[2] <= '9' && p[3])
198
if (strlen(p) + strlen(action) + 2 > sizeof(tbuf))
200
sprintf(tbuf, "%s%s%s", p, *action ? " " : "", action);
201
sprintf(sbuf, "%d:%d", splashpos - n, n);
204
if (pid == (pid_t)-1)
211
while (dup2(2, 1) < 0 && (errno == EINTR))
214
execl("/sbin/splash", "splash", "-p", sbuf, "-t", tbuf, splashcfg, (char *)0);
218
void writebuf(struct prg *p)
225
r = write(2, b, p->len);
237
static int checksystem(const int par, const boolean start, const boolean limit)
239
const int pg_size = sysconf(_SC_PAGESIZE);
240
const long int minphys_bytes = (sysconf(_SC_LONG_BIT) > 32L) ? (2<<22) : (2<<21);
241
const long int avphys_pg = sysconf(_SC_AVPHYS_PAGES);
243
unsigned long int prcs_run, prcs_blked;
253
minphys_pg = avphys_pg;
255
minphys_pg = minphys_bytes / pg_size;
257
if (avphys_pg < minphys_pg)
266
if (read_proc(&prcs_run, &prcs_blked))
269
/* if we have preload running, we expect I/O not to be a problem */
270
if (ispreload != NoPreload)
273
newpar = (par*numcpu) - prcs_run + 1; /* +1 for startpar its self */
274
newpar -= (int)(((double)prcs_blked)*iorate); /* I/O load reduction */
277
fprintf(stderr, "checksystem par=%d newpar=%d (prcs_run=%lu) %ld\n", par, newpar, prcs_run, time(0));
286
static inline int checklimit(const int par, const boolean start)
288
return checksystem(par, start, true);
291
static inline int checkpar(const int par, const boolean start)
293
return checksystem(par, start, false);
296
#define SOCK_PATH "/dev/shm/preload_sock"
298
void run(struct prg *p)
301
pid_t parent = getpid();
310
fprintf(stderr, "could not get pty for %s\n", p->name);
312
else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))
314
fprintf(stderr, "could not init pty for %s\n", p->name);
318
if ((p->pid = fork()) == (pid_t)-1)
321
fprintf(stderr, "could not fork %s\n", p->name);
333
(void)signal(SIGINT, SIG_DFL);
334
(void)signal(SIGQUIT, SIG_DFL);
335
(void)signal(SIGSEGV, SIG_DFL);
336
(void)signal(SIGTERM, SIG_DFL);
337
(void)signal(SIGCHLD, SIG_DFL);
344
while (close(1) < 0 && (errno == EINTR))
346
if (open(m, O_RDWR) != 1)
351
while (dup2(1, 2) < 0 && (errno == EINTR))
353
tio.c_oflag &= ~OPOST;
354
if (tcsetattr(1, TCSANOW, &tio))
357
ioctl(1, TIOCSWINSZ, &wz);
363
while (dup2(2, 1) < 0 && (errno == EINTR))
369
if (!strcmp(arg, "start"))
373
struct sockaddr_un remote;
376
s = socket(AF_UNIX, SOCK_STREAM, 0);
379
memset(&remote, 0, sizeof(struct sockaddr_un));
380
remote.sun_family = AF_UNIX;
381
strcpy(remote.sun_path, SOCK_PATH);
382
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
384
t = connect(s, (struct sockaddr *)&remote, len);
387
if (ispreload != Preload)
388
kill(parent, SIGUSR1);
389
send(s, p->name, strlen(p->name), 0);
390
recv(s, str, 100, 0);
392
else if ( ispreload == Unknown)
395
* if we connected to preload once, we know it ran.
396
* In case we can't connect to it later, it means it did
397
* its job and we can guess I/O is no longer a problem.
399
kill(parent, SIGUSR2);
403
* if we use preload, we fork again to make bootcharts easier to read.
404
* The reason is that the name of the init script will otherwise be used
405
* when in reality the above code waited for preload. If we fork away
406
* before the exec, the waiting code will be folded into startpar
411
int ret = waitpid(child, &status, 0);
414
exit(WEXITSTATUS(status));
422
snprintf(path, sizeof(path), "/etc/init.d/%s", p->name);
423
execlp(path, p->arg0, arg, (char *)0);
426
execlp(p->name, p->arg0, arg, (char *)0);
428
execlp(p->name, p->arg0, (char *)0);
433
int run_single(const char *prg, const char *arg0, int spl)
438
if ((pid = fork()) == (pid_t)-1)
441
fprintf(stderr, "could not fork %s\n", prg);
447
(void)signal(SIGINT, SIG_DFL);
448
(void)signal(SIGQUIT, SIG_DFL);
449
(void)signal(SIGSEGV, SIG_DFL);
450
(void)signal(SIGTERM, SIG_DFL);
451
(void)signal(SIGCHLD, SIG_DFL);
453
while (dup2(2, 1) < 0 && (errno == EINTR))
459
snprintf(path, sizeof(path), "/etc/init.d/%s", prg);
460
execlp(path, arg0 ? arg0 : path, arg, (char *)0);
463
execlp(prg, arg0 ? arg0 : prg, arg, (char *)0);
465
execlp(prg, arg0 ? arg0 : prg, (char *)0);
470
while ((waitpid(pid, &r, 0) == (pid_t)-1) && (errno == EINTR))
472
callsplash(spl, prg, arg);
473
return WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
476
void do_forward(void)
481
while ((r = read(0, buf, sizeof(buf))) > 0)
498
static char *gtimo_buf;
499
static size_t gtimo_bufsize;
500
static size_t gtimo_buflen;
502
void storebuf(struct prg *p)
504
if ((gtimo_buflen + p->len) > gtimo_bufsize)
506
writebuf(p); /* In case of overflow or memory shortage */
510
(void)memcpy(gtimo_buf + gtimo_buflen, p->buf, p->len);
511
gtimo_buflen += p->len;
518
size_t len = gtimo_buflen;
519
char * buf = gtimo_buf;
522
return; /* In case of memory shortage */
526
int r = write(2, buf, len);
542
void detach(struct prg *p, const int store)
545
int flags = fcntl(p->fd, F_GETFL);
552
fcntl(p->fd, F_SETFL, flags);
553
while ((r = read(p->fd, p->buf, sizeof(p->buf))) > 0)
562
fcntl(p->fd, F_SETFL, flags);
563
if (r == -1 && errno == EWOULDBLOCK)
565
if ((r = fork()) == 0)
567
while (dup2(p->fd, 0) < 0 && (errno == EINTR))
569
while (dup2(2, 1) < 0 && (errno == EINTR))
572
execlp(myname, myname, "-f", "--", p->name, NULL);
582
static void sigchld(int sig __attribute__ ((unused)))
585
write(pidpipe[1], &c, 1);
588
static void sigwinch(int sig __attribute__ ((unused)))
590
if (ioctl(0, TIOCGWINSZ, &wz) < 0)
595
if (wz.ws_row == 0) wz.ws_row = 24;
596
if (wz.ws_col == 0) wz.ws_col = 80;
597
snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d", wz.ws_row);
598
snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
601
void usage(int status)
603
fprintf(stderr, "usage: startpar [options] [-a arg] prgs\n");
604
fprintf(stderr, " run given programs parallel\n");
605
fprintf(stderr, " startpar [options] [-P prev] [-R run] [-S <start>:<num>] -M mode\n");
606
fprintf(stderr, " run parallel with Makefile\n");
607
fprintf(stderr, " startpar -v\n");
608
fprintf(stderr, " show version number\n");
609
fprintf(stderr, "general options:\n");
610
fprintf(stderr, " -p parallel tasks\n");
611
fprintf(stderr, " -t I/O timeout\n");
612
fprintf(stderr, " -T global I/O timeout\n");
616
int main(int argc, char **argv)
629
struct prg *gtimo_running = 0;
630
struct prg *interactive_task = NULL;
631
char *prev_level = getenv("PREVLEVEL");
632
char *run_level = getenv("RUNLEVEL");
635
(void)signal(SIGUSR1, sighandler_preload);
636
(void)signal(SIGUSR2, sighandler_nopreload);
638
(void)signal(SIGCHLD, SIG_DFL);
639
numcpu = sysconf(_SC_NPROCESSORS_ONLN);
642
while ((c = getopt(argc, argv, "fhp:t:T:a:M:P:R:S:vi:")) != EOF)
653
gtimo = atoi(optarg);
674
printf("startpar version %s\n", VERSION);
680
iorate = atof(optarg);
696
char *so = strchr(splashopt, ':');
701
splashpos = atoi(splashopt);
704
splashcfg = getenv("SPLASHCFG");
714
if (!strcmp(run_mode, "boot"))
716
else if (!strcmp(run_mode, "halt"))
718
else if (!strcmp(run_mode, "start") || !strcmp(run_mode, "stop"))
721
if (!prev_level || !run_level)
723
fprintf(stderr, "You must specify previous and next runlevels\n");
729
fprintf(stderr, "invalid run mode %s\n", run_mode);
732
snprintf(makefile, sizeof(makefile), "/etc/init.d/.depend.%s", run_mode);
733
parse_makefile(makefile);
734
check_run_files(run_mode, prev_level, run_level);
736
argc = tree_entries; /* number of handled scripts */
737
isstart = !strcmp(arg, "start");
744
if (par > argc) /* not more than the number of all scripts */
747
inpar = par; /* the original argument of parallel procs per cpu */
749
par = checkpar(inpar, isstart); /* the number of parallel procs on all cpu's */
751
if (par > argc) /* not more than the number of all scripts */
754
nodevec = xcalloc(argc, sizeof(*nodevec));
762
isstart = !strcmp(arg, "start");
769
if (par > argc) /* not more than the number of all scripts */
772
inpar = par; /* the original argument of parallel procs per cpu */
774
par = checkpar(inpar, isstart); /* the number of parallel procs on all cpu's */
776
if (par > argc) /* not more than the number of all scripts */
781
resvec = (int *)xcalloc(argc, sizeof(int));
782
for (i = 0; i < argc; i++)
789
if ((*nodevec = pickup_task()))
791
*resvec = run_single((*nodevec)->name, (*nodevec)->arg0, calcsplash(0, 1, splashopt));
792
finish_task(*nodevec);
795
*resvec = run_single(*argv, *argv, calcsplash(0, 1, splashopt));
799
prgs = (struct prg *)xcalloc(par, sizeof *prgs);
800
gtimo_bufsize = par * PBUF_SIZE;
801
gtimo_buf = (char *) calloc(gtimo_bufsize, sizeof(char));
803
gtimo_bufsize = 0; /* Accept error due memory shortage */
805
sa.sa_handler = sigwinch;
806
sa.sa_flags = SA_RESTART|SA_NODEFER;
807
(void)sigemptyset(&sa.sa_mask);
808
if (sigaction(SIGWINCH, &sa, 0))
810
perror("sigwinch sigaction");
814
if (tcgetattr(0, &tio))
819
if (ioctl(0, TIOCGWINSZ, &wz) == 0)
821
if (wz.ws_row == 0) wz.ws_row = 24;
822
if (wz.ws_col == 0) wz.ws_col = 80;
824
strcat(&sz.env_row[0], "LINES=");
825
strcat(&sz.env_col[0], "COLUMNS=");
826
snprintf(sz.env_row, sizeof(sz.env_row), "LINES=%d", wz.ws_row);
827
snprintf(sz.env_col, sizeof(sz.env_col), "COLUMNS=%d", wz.ws_col);
834
fcntl(pidpipe[0], F_SETFL, FNONBLOCK);
835
fcntl(pidpipe[1], F_SETFL, FNONBLOCK);
836
sa.sa_handler = sigchld;
837
sa.sa_flags = SA_RESTART;
838
(void)sigemptyset(&sa.sa_mask);
839
if (sigaction(SIGCHLD, &sa, 0))
841
perror("sigchld sigaction");
845
gettimeofday(&glastio, 0);
846
limit = checklimit(inpar, isstart);
857
gettimeofday(&now, 0);
861
if ((diff = timerdiff(now, lastlim)) >= 300 || diff < 0)
864
fprintf(stderr, "%d: doing checklimit after %ldms %ld\n", getpid(), diff, time(0));
866
if ((limit = checklimit(inpar, isstart)) > argc)
867
limit = argc; /* not more than the number of all scripts */
872
fprintf(stderr, "par=%d, inpar=%d, limit=%d (diff=%ld)\n", par, inpar, limit, diff);
874
for (s = 0; s < par; s++) /* never leave this with break!! */
876
account: /* for the new process below */
878
if (p == interactive_task)
879
continue; /* don't count this here */
881
active++; /* count all running procs */
884
if (interactive_task)
885
continue; /* dont't start new processes */
887
continue; /* nothing to do */
891
continue; /* load balancing */
894
if ((nodevec[num] = pickup_task()) == NULL)
896
if (nodevec[num]->interactive)
897
interactive_task = p;
898
p->name = nodevec[num]->name;
899
p->arg0 = nodevec[num]->arg0 ? nodevec[num]->arg0 : nodevec[num]->name;
905
p->splashadd = calcsplash(num, argc, splashopt);
907
if (interactive_task)
908
continue; /* don't start this here */
914
finish_task(nodevec[p->num]);
916
gettimeofday(&now, 0);
918
goto account; /* take the new process into account */
922
FD_SET(p->fd, &rset);
927
if ((last < 0) || timercmp(&tv,&p->lastio,>))
932
} /* for (s = 0; s < par; s++) */
934
if (interactive_task)
938
p = interactive_task;
939
resvec[p->num] = run_single(p->name, p->arg0, p->splashadd);
941
finish_task(nodevec[p->num]);
944
interactive_task = NULL;
952
fprintf(stderr, "ERROR: not all processed (%d of %d)\n", num, argc);
954
if ((pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) > 0)
955
fprintf(stderr, "ERROR: not all processes are checked\n");
960
fprintf(stderr, "active = %d\n", active);
962
if (active == 1 && last >= 0)
965
if ((pid = waitpid(p->pid, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) == 0)
972
FD_SET(pidpipe[0], &rset);
973
/* drain the pidpipe */
974
while ((c = read(pidpipe[0], pipebuf, sizeof pipebuf)) > 0)
978
pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED);
982
if (pid == splashpid)
983
splashpid = (pid_t)0;
984
for (s = 0; s < par; s++)
991
if (WSTOPSIG(r) == SIGTTIN)
993
pid_t pg = getpgid(pid);
999
callsplash(p->splashadd, p->name, arg);
1000
resvec[p->num] = WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
1002
finish_task(nodevec[p->num]);
1004
if (gtimo_running == p)
1008
detach(p, GTIMO_OFFL);
1012
else if (gtimo_running)
1016
detach(p, GTIMO_USED);
1022
detach(p, GTIMO_OFFL);
1026
} /* for (s = 0; s < par; s++) */
1031
continue; /* start new processes? */
1037
if (gtimo >= 0 && !gtimo_running && last >= 0 && prgs[last].pid)
1039
struct timeval gl = glastio;
1041
if ((timo < 0) || timercmp(&tv,&gl,>))
1050
if (timo >= 0 || isgtimo)
1052
int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1053
struct timeval wait;
1055
timersub(&tv, &now, &wait);
1056
if (wait.tv_usec < 0)
1058
wait.tv_usec += 1000000;
1061
if (wait.tv_sec >= 0)
1063
int check = limit < par && num < argc;
1065
if (check) /* shorten timeout for new limit and procs ... */
1068
wait.tv_usec = (300 - diff) * 1000;
1071
fprintf(stderr, "going into select1 %d %ld %ld\n", last, wait.tv_sec, wait.tv_usec);
1073
r = select(setfd + 1, &rset, 0, 0, (last >= 0 || check) ? &wait : 0);
1075
if (check && (r == 0)) /* ... but do not throw out messages to early!!! */
1080
wait.tv_sec = 0; /* Avoid looping around (does this ever happen?) */
1081
wait.tv_usec = 20*1000;
1082
r = select(setfd + 1, &rset, 0, 0, last >= 0 ? &wait : 0);
1087
int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1088
r = select(setfd + 1, &rset, 0, 0, 0);
1100
if (last < 0) /* just in case... */
1104
if (isgtimo && p->pid)
1109
for (s = 0; s < par; s++)
1114
if (!FD_ISSET(p->fd, &rset))
1116
r = read(p->fd, p->buf + p->len, sizeof(p->buf) - p->len);
1119
if (!gtimo_running || p == gtimo_running)
1126
if (p->len == sizeof(p->buf))
1128
for (i = p->len - 1; i >= 0; i--)
1130
if (p->buf[i] == '\n')
1137
p->len = i; /* writebuf clears p->len */
1138
if (p->len < sizeof(p->buf))
1139
memmove(p->buf, p->buf + p->len, sizeof(p->buf) - p->len);
1140
p->len = sizeof(p->buf) - p->len;
1143
} /* for (s = 0; s < par; s++) */
1150
print_run_result(resvec, nodevec, run_mode);
1153
for (i = 0; i < argc; i++)
1156
if (resvec[i] == 255)
1158
fprintf(stderr, "ERROR: forgotten process??\n");
1163
printf(i ? " %d" : "%d", resvec[i]);
1164
#endif /* VERBOSE */
1168
#endif /* VERBOSE */