prep
[moreutils.git] / parallel.c
blobb9d7ab2a6aac22da55ce228bae7de6f2c7b03f51
1 /*
2 * parallel.c - run commands in parallel until you run out of commands
4 * Copyright © 2008 Tollef Fog Heen <tfheen@err.no>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 * USA
22 #define _GNU_SOURCE
23 #include <unistd.h>
24 #include <string.h>
25 #include <stdio.h>
26 #include <sys/time.h>
27 #include <time.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <sys/select.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <unistd.h>
35 #ifdef __sun
36 # include <sys/loadavg.h> /* getloadavg() */
37 #endif
39 #if !defined(WEXITED)
40 #define WEXITED 0
41 #endif
43 void usage() {
44 printf("parallel [OPTIONS] command -- arguments\n\tfor each argument, "
45 "run command with argument, in parallel\n");
46 printf("parallel [OPTIONS] -- commands\n\trun specified commands in parallel\n");
47 exit(1);
50 void exec_child(char **command, char **arguments, int replace_cb, int nargs) {
51 if (fork() != 0) {
52 return;
55 if (command[0]) {
56 char **argv;
57 int argc = 0;
58 int i;
59 char *s;
61 while (command[argc] != 0) {
62 argc++;
64 if (! replace_cb)
65 argc++;
66 argv = calloc(sizeof(char*), argc + nargs);
68 for (i = 0; i < argc; i++) {
69 while (replace_cb && (s=strstr(command[i], "{}"))) {
70 char *buf=malloc(strlen(command[i]) + strlen(arguments[0]));
71 s[0]='\0';
72 sprintf(buf, "%s%s%s", command[i], arguments[0], s+2);
73 command[i]=buf;
75 argv[i] = command[i];
77 if (! replace_cb)
78 memcpy(argv + i - 1, arguments, nargs * sizeof(char *));
79 execvp(argv[0], argv);
80 exit(1);
82 else {
83 int ret=system(arguments[0]);
84 if (WIFEXITED(ret)) {
85 exit(WEXITSTATUS(ret));
87 else {
88 exit(1);
91 return;
94 int wait_for_child(int options) {
95 id_t id_ignored = 0;
96 siginfo_t infop;
98 infop.si_pid = 0;
99 waitid(P_ALL, id_ignored, &infop, WEXITED | options);
100 if (infop.si_pid == 0) {
101 return -1; /* Nothing to wait for */
103 if (infop.si_code == CLD_EXITED) {
104 return infop.si_status;
106 return 1;
109 int main(int argc, char **argv) {
110 int maxjobs = -1;
111 int curjobs = 0;
112 double maxload = -1;
113 int argsatonce = 1;
114 int opt;
115 char **command = calloc(sizeof(char*), argc);
116 char **arguments = NULL;
117 int argidx = 0;
118 int arglen = 0;
119 int cidx = 0;
120 int returncode = 0;
121 int replace_cb = 0;
122 char *t;
124 while ((argv[optind] && strcmp(argv[optind], "--") != 0) &&
125 (opt = getopt(argc, argv, "+hij:l:n:")) != -1) {
126 switch (opt) {
127 case 'h':
128 usage();
129 break;
130 case 'i':
131 replace_cb = 1;
132 break;
133 case 'j':
134 errno = 0;
135 maxjobs = strtoul(optarg, &t, 0);
136 if (errno != 0 || (t-optarg) != strlen(optarg)) {
137 fprintf(stderr, "option '%s' is not a number\n",
138 optarg);
139 exit(2);
141 break;
142 case 'l':
143 errno = 0;
144 maxload = strtod(optarg, &t);
145 if (errno != 0 || (t-optarg) != strlen(optarg)) {
146 fprintf(stderr, "option '%s' is not a number\n",
147 optarg);
148 exit(2);
150 break;
151 case 'n':
152 errno = 0;
153 argsatonce = strtoul(optarg, &t, 0);
154 if (errno != 0 || argsatonce < 1 || (t-optarg) != strlen(optarg)) {
155 fprintf(stderr, "option '%s' is not a positive number\n",
156 optarg);
157 exit(2);
159 break;
160 default: /* ’?’ */
161 usage();
162 break;
166 if (replace_cb && argsatonce > 1) {
167 fprintf(stderr, "options -i and -n are incomaptible\n");
168 exit(2);
171 if (maxjobs < 0) {
172 #ifdef _SC_NPROCESSORS_ONLN
173 maxjobs = sysconf(_SC_NPROCESSORS_ONLN);
174 #else
175 #warning Cannot autodetect number of CPUS on this system: _SC_NPROCESSORS_ONLN not defined.
176 maxjobs = 1;
177 #endif
180 while (optind < argc) {
181 if (strcmp(argv[optind], "--") == 0) {
182 int i;
184 optind++;
185 arglen = argc - optind;
186 arguments = calloc(sizeof(char *), arglen);
187 if (! arguments) {
188 exit(1);
191 for (i = 0; i < arglen; i++) {
192 arguments[i] = strdup(argv[optind + i]);
194 optind += i;
196 else {
197 command[cidx] = strdup(argv[optind]);
198 cidx++;
200 optind++;
203 if (argsatonce > 1 && ! command[0]) {
204 fprintf(stderr, "option -n cannot be used without a command\n");
205 exit(2);
208 while (argidx < arglen) {
209 double load;
211 getloadavg(&load, 1);
213 if ((maxjobs == 0 || curjobs < maxjobs) &&
214 (maxload < 0 || load < maxload)) {
216 if (argsatonce > arglen - argidx)
217 argsatonce = arglen - argidx;
218 exec_child(command, arguments + argidx,
219 replace_cb, argsatonce);
220 argidx += argsatonce;
221 curjobs++;
224 if (maxjobs == 0 || curjobs == maxjobs) {
225 returncode |= wait_for_child(0);
226 curjobs--;
229 if (maxload > 0 && load >= maxload) {
230 int r;
231 sleep(1); /* XXX We should have a better
232 * heurestic than this */
233 r = wait_for_child(WNOHANG);
234 if (r > 0)
235 returncode |= r;
236 if (r != -1)
237 curjobs--;
240 while (curjobs > 0) {
241 returncode |= wait_for_child(0);
242 curjobs--;
245 return returncode;