support -n option
[moreutils.git] / parallel.c
blob99a9727e0937adc456063826d1caeb581e23bd7d
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 void usage() {
36 printf("parallel [OPTIONS] command -- arguments: for each argument, "
37 "run command with argument\n");
38 exit(1);
41 void exec_child(char **command, char **arguments, int replace_cb, int nargs) {
42 char **argv;
43 int argc = 0;
44 int i;
46 while (command[argc] != 0) {
47 argc++;
49 if (replace_cb == 0)
50 argc++;
51 argv = calloc(sizeof(char*), argc + nargs);
52 for (i = 0; i < argc; i++) {
53 argv[i] = command[i];
54 if (replace_cb && (strcmp(argv[i], "{}") == 0))
55 argv[i] = arguments[0];
57 if (replace_cb == 0)
58 memcpy(argv + i - 1, arguments, nargs * sizeof(char *));
59 if (fork() == 0) {
60 /* Child */
61 execvp(argv[0], argv);
62 exit(1);
64 return;
67 int wait_for_child(int options) {
68 id_t id_ignored = 0;
69 siginfo_t infop;
71 infop.si_pid = 0;
72 waitid(P_ALL, id_ignored, &infop, WEXITED | options);
73 if (infop.si_pid == 0)
74 return -1; /* Nothing to wait for */
75 if (infop.si_code == CLD_EXITED)
76 return infop.si_status;
77 return 1;
80 int main(int argc, char **argv) {
81 int maxjobs = -1;
82 int curjobs = 0;
83 int maxload = -1;
84 int argsatonce = 1;
85 int opt;
86 char **command = calloc(sizeof(char*), argc);
87 char **arguments = NULL;
88 int argidx = 0;
89 int arglen = 0;
90 int cidx = 0;
91 int returncode = 0;
92 int replace_cb = 0;
93 char *t;
95 while ((opt = getopt(argc, argv, "+hij:l:n:")) != -1) {
96 switch (opt) {
97 case 'h':
98 usage();
99 break;
100 case 'i':
101 replace_cb = 1;
102 break;
103 case 'j':
104 errno = 0;
105 maxjobs = strtoul(optarg, &t, 0);
106 if (errno != 0 || (t-optarg) != strlen(optarg)) {
107 fprintf(stderr, "option '%s' is not a number\n",
108 optarg);
109 exit(2);
111 break;
112 case 'l':
113 errno = 0;
114 maxload = strtoul(optarg, &t, 0);
115 if (errno != 0 || (t-optarg) != strlen(optarg)) {
116 fprintf(stderr, "option '%s' is not a number\n",
117 optarg);
118 exit(2);
120 break;
121 case 'n':
122 errno = 0;
123 argsatonce = strtoul(optarg, &t, 0);
124 if (errno != 0 || argsatonce < 1 || (t-optarg) != strlen(optarg)) {
125 fprintf(stderr, "option '%s' is not a positive number\n",
126 optarg);
127 exit(2);
129 break;
130 default: /* ’?’ */
131 usage();
132 break;
136 if (replace_cb && argsatonce > 1) {
137 fprintf(stderr, "options -i and -n are incomaptible\n");
138 exit(2);
141 if (maxjobs < 0) {
142 #ifdef _SC_NPROCESSORS_ONLN
143 maxjobs = sysconf(_SC_NPROCESSORS_ONLN);
144 #else
145 #warning Cannot autodetect number of CPUS on this system: _SC_NPROCESSORS_ONLN not defined.
146 maxjobs = 1;
147 #endif
150 while (optind < argc) {
151 if (strcmp(argv[optind], "--") == 0) {
152 int i;
154 optind++;
155 arglen = argc - optind;
156 arguments = calloc(sizeof(char *), arglen);
157 if (! arguments) {
158 exit(1);
161 for (i = 0; i < arglen; i++) {
162 arguments[i] = strdup(argv[optind + i]);
164 optind += i;
166 else {
167 command[cidx] = strdup(argv[optind]);
168 cidx++;
170 optind++;
173 while (argidx < arglen) {
174 double load;
176 getloadavg(&load, 1);
178 if ((maxjobs > 0 && curjobs < maxjobs) ||
179 (maxload > 0 && load < maxload)) {
180 if (argsatonce > arglen - argidx)
181 argsatonce = arglen - argidx;
182 exec_child(command, arguments + argidx,
183 replace_cb, argsatonce);
184 argidx += argsatonce;
185 curjobs++;
188 if (maxjobs > 0 && curjobs == maxjobs) {
189 returncode |= wait_for_child(0);
190 curjobs--;
193 if (maxload > 0 && load > maxload) {
194 int r;
195 sleep(1); /* XXX We should have a better
196 * heurestic than this */
197 r = wait_for_child(WNOHANG);
198 if (r > 0) {
199 returncode |= r;
200 curjobs--;
204 while (curjobs > 0) {
205 returncode |= wait_for_child(0);
206 curjobs--;
209 return returncode;