~ubuntu-branches/debian/experimental/sysvinit/experimental

« back to all changes in this revision

Viewing changes to .pc/71_dev_pts_workaround.patch/startpar/startpar.c

  • Committer: Package Import Robot
  • Author(s): Roger Leigh, Roger Leigh, Steve Langasek
  • Date: 2012-04-21 12:11:45 UTC
  • mfrom: (9.1.33 sid)
  • Revision ID: package-import@ubuntu.com-20120421121145-tcyy57v1j6gx946d
Tags: 2.88dsf-23
[ Roger Leigh ]
* Acknowledge NMU for translation updates.  Thanks to Christian
  Perrier.
* debian/control:
  - Upgrade to Standards-Version 3.9.3.
  - Build-Depend on debhelper v9.
  - Correct Vcs-Git URL.
* debian/rules:
  - Use DEB_HOST_ARCH_OS = hurd rather than
    DEB_HOST_ARCH = hurd-i386.   Thanks to Pino Toscano.
* debian/patches:
  - 11_lfs_cflags.patch: Add patch for enabling large file support,
    needed on GNU/Hurd, but useful for all platforms.
  - 73_lfs_cflags.patch: Add patch for enabling large file support
    in startpar.
* initscripts:
  - Moved RAM* settings from /etc/default/rcS to /etc/default/tmpfs.
    This ensures that the settings are equivalent for upgrades and
    new installations, but will require manual configuration of the
    settings for upgrades (no migration from /etc/default/rcS to
    /etc/default/tmpfs will take place, due to tmpfs being a
    conffile).  tmpf(5) manual page added to document all aspects
    of tmpfs configuration, including the existing documentation in
    rcS(5).
  - Drop the use of .ramfs dotfiles in /run and /run/lock.  These
    were a legacy of /lib/init/rw and were not actually used by
    anything.  Closes: #403863.
  - Drop /etc/init.d/mountoverflowtmp.  This has been merged into
    the general tmpfs on /tmp handling functions.  This means the
    generic RAMTMP configuration is used for the overflowtmp.
    Closes: #567539.
  - It is now possible to configure a tmpfs mount size limit as a
    percentage of the total VM size (%VM) as well as a percentage
    of the RAM size (%).  This is computed by tmpfs.sh and the
    tmpfs mounts are remounted with the updated size limit after
    swap becomes available.
  - An fstab entry for /tmp overrides RAMTMP.  Document tmpfs
    override and tmpfs defaults in tmpfs(5), also undeprecating the
    tmpfs settings.  Closes: #585540, #665995.
  - An fstab entry for /run/lock or /run/shm overrides RAMLOCK and
    RAMSHM.
  - bootclean cleans /tmp, /run and /run/lock before any filesystems
    are mounted as well as after local and network mounts.  This
    permits cleaning of directories which would otherwise be hidden
    by mountpoints later in the boot process.
    Closes: #55707, #558000, #666871.  Additionally clean up
    /lib/init/rw in case any files were hidden by the (now removed)
    tmpfs mount at this location.  Closes: #652625.
  - Removed last trace of the long-removed EDITMOTD from the
    postinst.  Closes: #438895.
  - Removed documentation of #346342 in rcS(5).  This is no longer
    an issue now tzdata keeps a copy of the data on the rootfs.
    Closes: #385172.
  - Correct description of TMPTIME in rcS(5).  Thanks to Alan J.
    Greenberger.  Closes: #562500.
  - urandom: Applied a series of patches from John Denker to
    improve the integrity of random number generation.  Many thanks.
    Closes: #596479, #596480, #596481, #596482, #596483.
* sysv-rc:
  - Remove old upgrade logic from maintainer scripts not required
    for wheezy.
  - Migrate users of obsolete static boot ordering to dynamic boot
    ordering.
  - Remove use of /etc/init.d/.legacy-bootordering.  Closes: #668312.
  - Improve help text of debconf message when it is not possible to
    automatically enable dynamic boot ordering.  Provide explicit
    instructions for how to purge obsolete init scripts.
    Closes: #550425.
  - etc/init.d/rc: Ensure linprocfs is mounted on kFreeBSD.  Thanks
    to Robert Millan.  Closes: #659480.
  - Drop undocumented CONCURRENCY setting from /etc/init.d/rc.
    Closes: #518249, #540448, #539261.  Note that this still contains
    internal fallbacks to support non-insserv booting, which may be
    removed at a later date.
  - invoke-rc.d:
    + Minor manual page corrections.  Thanks to Anthony Fiumara.
      Closes: #664816.
    + Remove mention of the "dpkg Programmers' Manual" and replace
      with references to Debian Policy.  Closes: #543793.
  - update-rc.d:
    + Correctly warn about non-LSB standard runlevels.  Thanks to
      Chris Hiestand for this patch.  Closes: #614895.
    + Remove obsolete documentation of
      /var/lib/sysv-rc/legacy-bootsequence.  Thanks to Thomas Hood.
      Closes: #623051.
* sysvinit:
  - Minor corrections for halt(8) manual page.  Thanks to
    Christoph Anton Mitterer.  Closes: #587923.
  - Installation with debootstrap --variant=fakechroot now works, due
    to only migrating the old control channel when it is still
    present.  Thanks to Michael Gilbert.  Closes: #596284.
* sysvinit-utils:
  - Recommend bootlogd.  Closes: #659490.  This means that booklogd
    will be installed by default, but will be removable.
    Closes: #232569.
  - Correct documentation of the startpar -i option.  Closes: #545438.
  - Correct startpar(8) SEE ALSO section.  Closes: #634146.
  - Correct wording in service(8).  Thanks to Joey Hess and Regid
    Ichira.  Closes: #545401, #667745.

[ Steve Langasek ]
* debian/service/service: fix upstart compatibility to not try to use the
  upstart commands when init isn't upstart.  Closes: #636054.
* debian/rules: pass CFLAGS when building startpar.
* Fix startpar to not run init scripts that have matching upstart jobs,
  instead waiting for a signal from upstart.  Closes: #660824.
    to Robert Millan.  (Closes: #659480)
* sysvinit:
  - Don't restart or perform initctl migration if systemd is
    running.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright (c) 2003 SuSE Linux AG
2
 
 *
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)
6
 
 * any later version.
7
 
 *  
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.
12
 
 *
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,
16
 
 * MA 02110-1301, USA.
17
 
 *
18
 
 ****************************************************************
19
 
 */
20
 
 
21
 
#ifndef _GNU_SOURCE
22
 
#define _GNU_SOURCE 1
23
 
#endif
24
 
 
25
 
#include <stdio.h>
26
 
#include <termios.h>
27
 
#include <sys/types.h>
28
 
#include <sys/wait.h>
29
 
#include <sys/select.h>
30
 
#include <sys/time.h>
31
 
#include <sys/ioctl.h>
32
 
#include <sys/socket.h>
33
 
#include <sys/un.h>
34
 
#include <sys/sysinfo.h>
35
 
#include <sys/stat.h>
36
 
#include <time.h>
37
 
#include <fcntl.h>
38
 
#include <errno.h>
39
 
#include <string.h>
40
 
#include <stdlib.h>
41
 
#include <unistd.h>
42
 
#include "makeboot.h"
43
 
#include "proc.h"
44
 
 
45
 
#define timerdiff(n,l) (__extension__ ({ (((n).tv_sec-(l).tv_sec)*1000)+(((n).tv_usec-(l).tv_usec)/1000); }))
46
 
 
47
 
typedef enum _boolean {false, true} boolean;
48
 
extern char *optarg;
49
 
extern int optind;
50
 
 
51
 
static long int numcpu = -1;
52
 
static char *myname;
53
 
static struct termios tio;
54
 
static struct winsize wz;
55
 
static struct {
56
 
  char env_row[128];
57
 
  char env_col[128];
58
 
} sz;
59
 
static sig_atomic_t wzok;
60
 
static char *arg;
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;
68
 
 
69
 
static enum { Unknown, Preload, NoPreload } ispreload = Unknown;
70
 
 
71
 
#define PBUF_SIZE       8192
72
 
struct prg {
73
 
  const char *name;
74
 
  const char *arg0;
75
 
  int num;
76
 
  int fd;
77
 
  pid_t pid;
78
 
  struct timeval lastio;
79
 
  size_t len;
80
 
  int splashadd;
81
 
  char buf[PBUF_SIZE];
82
 
};
83
 
 
84
 
static struct prg *prgs;
85
 
static int inpar, par;
86
 
static int pidpipe[2];
87
 
static double iorate = 800.0;
88
 
 
89
 
void sighandler_nopreload(int x)
90
 
{
91
 
    (void)x;
92
 
    ispreload = NoPreload;
93
 
}
94
 
 
95
 
 
96
 
void sighandler_preload(int x)
97
 
{
98
 
    (void)x;
99
 
    ispreload = Preload;
100
 
}
101
 
 
102
 
void *xcalloc(size_t nmemb, size_t size)
103
 
{
104
 
  void *r;
105
 
  if ((r = (void *)calloc(nmemb, size)) == 0)
106
 
    {
107
 
      fprintf(stderr, "calloc: out of memory\n");
108
 
      exit(1);
109
 
    }
110
 
  return r;
111
 
}
112
 
 
113
 
static int splashpos = -1;
114
 
static char *splashcfg;
115
 
 
116
 
int calcsplash(int x, int n, char *opt)
117
 
{
118
 
  char *p;
119
 
  int i;
120
 
  int r, d;
121
 
 
122
 
  if (opt == 0)
123
 
    return -1;
124
 
  for (i = 0; i <= x; i++)
125
 
    {
126
 
      if ((p = strchr(opt, ':')) == 0)
127
 
        break;
128
 
      if (i == x)
129
 
        return atoi(opt);
130
 
      opt = p + 1;
131
 
    }
132
 
  r = atoi(opt);
133
 
  n -= i;
134
 
  for (;; i++, n--)
135
 
    {
136
 
      if (n < 1)
137
 
        n = 1;
138
 
      d = r / n;
139
 
      if (i == x)
140
 
        return d;
141
 
      r -= d;
142
 
    }
143
 
}
144
 
 
145
 
pid_t splashpid;
146
 
 
147
 
void waitsplash()
148
 
{
149
 
  int status;
150
 
  if (!splashpid)
151
 
    return;
152
 
  do {
153
 
    waitpid(splashpid, &status, 0);
154
 
  } while (errno == EINTR);
155
 
  splashpid = 0;
156
 
}
157
 
 
158
 
void closeall(void)
159
 
{
160
 
  int s;
161
 
 
162
 
  if (!prgs)
163
 
    return;
164
 
  for (s = 0; s < par; s++)
165
 
    if (prgs[s].fd)
166
 
      close(prgs[s].fd);
167
 
  close(pidpipe[0]);
168
 
  close(pidpipe[1]);
169
 
}
170
 
 
171
 
void callsplash(int n, const char *path, char *action)
172
 
{
173
 
  const char *p;
174
 
  char sbuf[32];
175
 
  char tbuf[256];
176
 
  pid_t pid;
177
 
  struct stat stb;
178
 
 
179
 
  if (n < 0 || splashpos < 0)
180
 
    return;
181
 
  if (splashpos + n > 65535)
182
 
    n = 65535 - splashpos;
183
 
  splashpos += n;
184
 
  if (stat("/proc/splash", &stb))
185
 
     return;
186
 
  p = strrchr(path, '/');
187
 
  if (p)
188
 
    path = p + 1;
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])
191
 
      break;
192
 
  if (*p)
193
 
    p += 3;
194
 
  else
195
 
    p = path;
196
 
  if (!action)
197
 
    action = "";
198
 
  if (strlen(p) + strlen(action) + 2 > sizeof(tbuf))
199
 
    return;
200
 
  sprintf(tbuf, "%s%s%s", p, *action ? " " : "", action);
201
 
  sprintf(sbuf, "%d:%d", splashpos - n, n);
202
 
  waitsplash();
203
 
  pid = fork();
204
 
  if (pid == (pid_t)-1)
205
 
    return;
206
 
  if (pid)
207
 
    {
208
 
      splashpid = pid;
209
 
      return;
210
 
    }
211
 
  while (dup2(2, 1) < 0 && (errno == EINTR))
212
 
    ;
213
 
  closeall();
214
 
  execl("/sbin/splash", "splash", "-p", sbuf, "-t", tbuf, splashcfg, (char *)0);
215
 
  _exit(1);
216
 
}
217
 
 
218
 
void writebuf(struct prg *p)
219
 
{
220
 
  char *b = p->buf;
221
 
  int r;
222
 
 
223
 
  while (p->len > 0)
224
 
    {
225
 
      r = write(2, b, p->len);
226
 
      if (r < 0)
227
 
        {
228
 
          perror("write");
229
 
          r = p->len;
230
 
        }
231
 
      p->len -= r;
232
 
      b += r;
233
 
    }
234
 
  glastio = now;
235
 
}
236
 
 
237
 
static int checksystem(const int par, const boolean start, const boolean limit)
238
 
{
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);
242
 
  long int minphys_pg;
243
 
  unsigned long int prcs_run, prcs_blked;
244
 
  int newpar;
245
 
  
246
 
  if (avphys_pg < 0)
247
 
    return 1;
248
 
 
249
 
  if (pg_size < 0)
250
 
    return par;
251
 
 
252
 
  if (!start)
253
 
    minphys_pg = avphys_pg;
254
 
  else
255
 
    minphys_pg = minphys_bytes / pg_size;
256
 
 
257
 
  if (avphys_pg < minphys_pg)
258
 
    return 1;
259
 
 
260
 
  if (numcpu < 1)
261
 
    return par;
262
 
  
263
 
  if (!limit)
264
 
    return (par*numcpu);
265
 
 
266
 
  if (read_proc(&prcs_run, &prcs_blked))
267
 
    return par;
268
 
 
269
 
  /* if we have preload running, we expect I/O not to be a problem */
270
 
  if (ispreload != NoPreload)
271
 
    prcs_blked = 0;
272
 
 
273
 
  newpar  = (par*numcpu) - prcs_run + 1;        /* +1 for startpar its self */
274
 
  newpar -= (int)(((double)prcs_blked)*iorate); /* I/O load reduction */
275
 
 
276
 
#if DEBUG
277
 
  fprintf(stderr, "checksystem par=%d newpar=%d (prcs_run=%lu) %ld\n", par, newpar, prcs_run, time(0));
278
 
  dump_status();
279
 
#endif
280
 
  if (newpar <= 0)
281
 
    return 1;
282
 
  else
283
 
    return newpar;
284
 
}
285
 
 
286
 
static inline int checklimit(const int par, const boolean start)
287
 
{
288
 
  return checksystem(par, start, true);
289
 
}
290
 
 
291
 
static inline int checkpar(const int par, const boolean start)
292
 
{
293
 
  return checksystem(par, start, false);
294
 
}
295
 
 
296
 
#define SOCK_PATH "/dev/shm/preload_sock"
297
 
 
298
 
void run(struct prg *p)
299
 
{
300
 
  char *m = 0;
301
 
  pid_t parent = getpid();
302
 
 
303
 
  p->len = 0;
304
 
  p->pid = (pid_t)0;
305
 
  p->fd = getpt();
306
 
  if (p->fd <= 0)
307
 
    {
308
 
      p->fd = 0;
309
 
      perror("getpt");
310
 
      fprintf(stderr, "could not get pty for %s\n", p->name);
311
 
    }
312
 
  else if ((m = ptsname(p->fd)) == 0 || grantpt(p->fd) || unlockpt(p->fd))
313
 
    {
314
 
      fprintf(stderr, "could not init pty for %s\n", p->name);
315
 
      close(p->fd);
316
 
      p->fd = 0;
317
 
    }
318
 
  if ((p->pid = fork()) == (pid_t)-1)
319
 
    {
320
 
      perror("fork");
321
 
      fprintf(stderr, "could not fork %s\n", p->name);
322
 
      p->pid = 0;
323
 
      if (p->fd)
324
 
        {
325
 
          close(p->fd);
326
 
          p->fd = 0;
327
 
        }
328
 
      return;
329
 
    }
330
 
  if (p->pid != 0)
331
 
    return;
332
 
 
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);
338
 
 
339
 
  if (setpgid(0, 0))
340
 
    perror("setpgid");
341
 
 
342
 
  if (m && p->fd)
343
 
    {
344
 
      while (close(1) < 0 && (errno == EINTR))
345
 
        ;
346
 
      if (open(m, O_RDWR) != 1)
347
 
        {
348
 
          perror(m);
349
 
          _exit(1);
350
 
        }
351
 
      while (dup2(1, 2) < 0 && (errno == EINTR))
352
 
        ;
353
 
      tio.c_oflag &= ~OPOST;
354
 
      if (tcsetattr(1, TCSANOW, &tio))
355
 
        perror("tcsetattr");
356
 
      if (wzok)
357
 
        ioctl(1, TIOCSWINSZ, &wz);
358
 
      putenv(sz.env_row);
359
 
      putenv(sz.env_col);
360
 
    }
361
 
  else
362
 
    {
363
 
      while (dup2(2, 1) < 0 && (errno == EINTR))
364
 
        ;
365
 
    }
366
 
 
367
 
  closeall();
368
 
 
369
 
  if (!strcmp(arg, "start")) 
370
 
    { 
371
 
      int s, t, len;
372
 
      pid_t child;
373
 
      struct sockaddr_un remote;
374
 
      char str[100];
375
 
 
376
 
      s = socket(AF_UNIX, SOCK_STREAM, 0);
377
 
      if (s != -1) 
378
 
        {
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);
383
 
 
384
 
          t = connect(s, (struct sockaddr *)&remote, len);
385
 
          if (t != -1) 
386
 
            {
387
 
              if (ispreload != Preload)
388
 
                kill(parent, SIGUSR1);
389
 
              send(s, p->name, strlen(p->name), 0);
390
 
              recv(s, str, 100, 0);
391
 
            } 
392
 
          else if ( ispreload == Unknown) 
393
 
            {
394
 
              /*
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. 
398
 
               */
399
 
              kill(parent, SIGUSR2);
400
 
            }
401
 
          close(s);
402
 
          /*
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
407
 
           */
408
 
          child = fork();
409
 
          if (child) {
410
 
                int status;
411
 
                int ret = waitpid(child, &status, 0);
412
 
                if (ret == -1)
413
 
                        perror("waitpid");
414
 
                exit(WEXITSTATUS(status));
415
 
          }
416
 
        }
417
 
    }
418
 
 
419
 
  if (run_mode)
420
 
    {
421
 
      char path[128];
422
 
      snprintf(path, sizeof(path), "/etc/init.d/%s", p->name);
423
 
      execlp(path, p->arg0, arg, (char *)0);
424
 
    }
425
 
  else if (arg)
426
 
    execlp(p->name, p->arg0, arg, (char *)0);
427
 
  else
428
 
    execlp(p->name, p->arg0, (char *)0);
429
 
  perror(p->name);
430
 
  _exit(1);
431
 
}
432
 
 
433
 
int run_single(const char *prg, const char *arg0, int spl)
434
 
{
435
 
  pid_t pid;
436
 
  int r;
437
 
 
438
 
  if ((pid = fork()) == (pid_t)-1)
439
 
    {
440
 
      perror("fork");
441
 
      fprintf(stderr, "could not fork %s\n", prg);
442
 
      return 1;
443
 
    }
444
 
 
445
 
  if (pid == 0)
446
 
    {
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);
452
 
 
453
 
      while (dup2(2, 1) < 0 && (errno == EINTR))
454
 
        ;
455
 
      closeall();
456
 
      if (run_mode)
457
 
        {
458
 
          char path[128];
459
 
          snprintf(path, sizeof(path), "/etc/init.d/%s", prg);
460
 
          execlp(path, arg0 ? arg0 : path, arg, (char *)0);
461
 
        }
462
 
      else if (arg)
463
 
        execlp(prg, arg0 ? arg0 : prg, arg, (char *)0);
464
 
      else
465
 
        execlp(prg, arg0 ? arg0 : prg, (char *)0);
466
 
      perror(prg);
467
 
      _exit(1);
468
 
    }
469
 
 
470
 
   while ((waitpid(pid, &r, 0) == (pid_t)-1) && (errno == EINTR))
471
 
     ;
472
 
   callsplash(spl, prg, arg);
473
 
   return WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
474
 
}
475
 
 
476
 
void do_forward(void)
477
 
{
478
 
  char buf[4096], *b;
479
 
  int r, rr;
480
 
  setsid();
481
 
  while ((r = read(0, buf, sizeof(buf))) > 0)
482
 
    {
483
 
      b = buf;
484
 
      while (r > 0)
485
 
        {
486
 
          rr = write(1, b, r);
487
 
          if (rr == -1)
488
 
            {
489
 
              perror("write");
490
 
              rr = r;
491
 
            }
492
 
          r -= rr;
493
 
        }
494
 
    }
495
 
  _exit(0);
496
 
}
497
 
 
498
 
static char *gtimo_buf;
499
 
static size_t gtimo_bufsize;
500
 
static size_t gtimo_buflen;
501
 
 
502
 
void storebuf(struct prg *p)
503
 
{
504
 
  if ((gtimo_buflen + p->len) > gtimo_bufsize)
505
 
    {
506
 
      writebuf(p);                              /* In case of overflow or memory shortage */
507
 
      return;
508
 
    }
509
 
 
510
 
  (void)memcpy(gtimo_buf + gtimo_buflen, p->buf, p->len);
511
 
  gtimo_buflen += p->len;
512
 
  p->len = 0;
513
 
  glastio = now;
514
 
}
515
 
 
516
 
void flushbuf(void)
517
 
{
518
 
  size_t len = gtimo_buflen;
519
 
  char * buf = gtimo_buf;
520
 
 
521
 
  if (!buf)
522
 
        return;                                 /* In case of memory shortage */
523
 
 
524
 
  while (len > 0)
525
 
    {
526
 
      int r = write(2, buf, len);
527
 
      if (r < 0)
528
 
        {
529
 
          perror("write");
530
 
          r = len;
531
 
        }
532
 
      len -= r;
533
 
      buf += r;
534
 
    }
535
 
  gtimo_buflen = 0;
536
 
  *gtimo_buf = 0;
537
 
}
538
 
 
539
 
#define GTIMO_OFFL      0
540
 
#define GTIMO_USED      1
541
 
 
542
 
void detach(struct prg *p, const int store)
543
 
{
544
 
  int r;
545
 
  int flags = fcntl(p->fd, F_GETFL);
546
 
 
547
 
  if (flags > 0)
548
 
    flags |= FNONBLOCK;
549
 
  else
550
 
    flags = FNONBLOCK;
551
 
 
552
 
  fcntl(p->fd, F_SETFL, flags);
553
 
  while ((r = read(p->fd, p->buf, sizeof(p->buf))) > 0)
554
 
    {
555
 
      p->len = r;
556
 
      if (store)
557
 
        storebuf(p);
558
 
      else
559
 
        writebuf(p);
560
 
    }
561
 
  flags &= ~FNONBLOCK;
562
 
  fcntl(p->fd, F_SETFL, flags);
563
 
  if (r == -1 && errno == EWOULDBLOCK)
564
 
    {
565
 
      if ((r = fork()) == 0)
566
 
        {
567
 
          while (dup2(p->fd, 0) < 0 && (errno == EINTR))
568
 
            ;
569
 
          while (dup2(2, 1) < 0 && (errno == EINTR))
570
 
            ;
571
 
          closeall();
572
 
          execlp(myname, myname, "-f", "--", p->name, NULL);
573
 
          do_forward();
574
 
        }
575
 
      if (r == -1)
576
 
        perror("fork");
577
 
    }
578
 
  close(p->fd);
579
 
  p->fd = 0;
580
 
}
581
 
 
582
 
static void sigchld(int sig __attribute__ ((unused)))
583
 
{
584
 
  char c = 0;
585
 
  write(pidpipe[1], &c, 1);
586
 
}
587
 
 
588
 
static void sigwinch(int sig __attribute__ ((unused)))
589
 
{
590
 
  if (ioctl(0, TIOCGWINSZ, &wz) < 0)
591
 
    {
592
 
      wzok = 0;
593
 
      return;
594
 
    }
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);
599
 
}
600
 
 
601
 
void usage(int status)
602
 
{
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");
613
 
  exit(status);
614
 
}
615
 
 
616
 
int main(int argc, char **argv)
617
 
{
618
 
  int gtimo = -1;
619
 
  int timo = -1;
620
 
  int isgtimo;
621
 
  int forw = 0;
622
 
  int c, i, num;
623
 
  int limit;
624
 
  int *resvec;
625
 
  fd_set rset;
626
 
  struct timeval tv;
627
 
  struct prg *p;
628
 
  char pipebuf[16];
629
 
  struct prg *gtimo_running = 0;
630
 
  struct prg *interactive_task = NULL;
631
 
  char *prev_level = getenv("PREVLEVEL");
632
 
  char *run_level = getenv("RUNLEVEL");
633
 
  char *splashopt = 0;
634
 
 
635
 
  (void)signal(SIGUSR1, sighandler_preload);
636
 
  (void)signal(SIGUSR2, sighandler_nopreload);
637
 
 
638
 
  (void)signal(SIGCHLD, SIG_DFL);
639
 
  numcpu = sysconf(_SC_NPROCESSORS_ONLN);
640
 
  myname = argv[0];
641
 
 
642
 
  while ((c = getopt(argc, argv, "fhp:t:T:a:M:P:R:S:vi:")) != EOF)
643
 
    {
644
 
      switch(c)
645
 
        {
646
 
        case 'p':
647
 
          par = atoi(optarg);
648
 
          break;
649
 
        case 't':
650
 
          timo = atoi(optarg);
651
 
          break;
652
 
        case 'T':
653
 
          gtimo = atoi(optarg);
654
 
          break;
655
 
        case 'f':
656
 
          forw = 1;
657
 
          break;
658
 
        case 'a':
659
 
          arg = optarg;
660
 
          break;
661
 
        case 'M':
662
 
          run_mode = optarg;
663
 
          break;
664
 
        case 'P':
665
 
          prev_level = optarg;
666
 
          break;
667
 
        case 'R':
668
 
          run_level = optarg;
669
 
          break;
670
 
        case 'S':
671
 
          splashopt = optarg;
672
 
          break;
673
 
        case 'v':
674
 
          printf("startpar version %s\n", VERSION);
675
 
          exit(0);
676
 
        case 'h':
677
 
          usage(0);
678
 
          break;
679
 
        case 'i':
680
 
          iorate = atof(optarg);
681
 
          if (iorate < 0.0)
682
 
            iorate = 800.0;
683
 
          break;
684
 
        default:
685
 
          usage(1);
686
 
          break;
687
 
        }
688
 
    }
689
 
  if (forw)
690
 
    do_forward();
691
 
  argc -= optind;
692
 
  argv += optind;
693
 
 
694
 
  if (splashopt)
695
 
    {
696
 
      char *so = strchr(splashopt, ':');
697
 
      if (!so)
698
 
        splashopt = 0;
699
 
      else
700
 
        {
701
 
          splashpos = atoi(splashopt);
702
 
          splashopt = so + 1;
703
 
        }
704
 
      splashcfg = getenv("SPLASHCFG");
705
 
      if (!splashcfg)
706
 
        {
707
 
          splashpos = -1;
708
 
          splashopt = 0;
709
 
        }
710
 
    }
711
 
  if (run_mode)
712
 
    {
713
 
      char makefile[64];
714
 
      if (!strcmp(run_mode, "boot"))
715
 
        arg = "start";
716
 
      else if (!strcmp(run_mode, "halt"))
717
 
        arg = "stop";
718
 
      else if (!strcmp(run_mode, "start") || !strcmp(run_mode, "stop"))
719
 
        {
720
 
          arg = run_mode;
721
 
          if (!prev_level || !run_level)
722
 
            {
723
 
              fprintf(stderr, "You must specify previous and next runlevels\n");
724
 
              exit(1);
725
 
            }
726
 
        }
727
 
      else
728
 
        {
729
 
          fprintf(stderr, "invalid run mode %s\n", run_mode);
730
 
          exit(1);
731
 
        }
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);
735
 
 
736
 
      argc = tree_entries;                      /* number of handled scripts */
737
 
      isstart = !strcmp(arg, "start");
738
 
 
739
 
      if (argc == 0)
740
 
        exit(0);
741
 
 
742
 
      if (par == 0)
743
 
        par = 4;
744
 
      if (par > argc)                           /* not more than the number of all scripts */
745
 
        par = argc;
746
 
 
747
 
      inpar = par;                              /* the original argument of parallel procs per cpu */
748
 
 
749
 
      par = checkpar(inpar, isstart);           /* the number of parallel procs on all cpu's */
750
 
 
751
 
      if (par > argc)                           /* not more than the number of all scripts */
752
 
        par = argc;
753
 
 
754
 
      nodevec = xcalloc(argc, sizeof(*nodevec));
755
 
    }
756
 
  else
757
 
    {
758
 
      if (par < 0)
759
 
        usage(1);
760
 
 
761
 
      if (arg)
762
 
        isstart = !strcmp(arg, "start");
763
 
 
764
 
      if (argc == 0)
765
 
        exit(0);
766
 
 
767
 
      if (par == 0)
768
 
        par = argc;
769
 
      if (par > argc)                           /* not more than the number of all scripts */
770
 
        par = argc;
771
 
 
772
 
      inpar = par;                              /* the original argument of parallel procs per cpu */
773
 
 
774
 
      par = checkpar(inpar, isstart);           /* the number of parallel procs on all cpu's */
775
 
 
776
 
      if (par > argc)                           /* not more than the number of all scripts */
777
 
        par = argc;
778
 
    }
779
 
 
780
 
  num = 0;
781
 
  resvec = (int *)xcalloc(argc, sizeof(int));
782
 
  for (i = 0; i < argc; i++)
783
 
    resvec[i] = 255;
784
 
 
785
 
  if (argc == 1)
786
 
    {
787
 
      if (run_mode)
788
 
        {
789
 
          if ((*nodevec = pickup_task()))
790
 
          {
791
 
            *resvec = run_single((*nodevec)->name, (*nodevec)->arg0, calcsplash(0, 1, splashopt));
792
 
            finish_task(*nodevec);
793
 
          }
794
 
      } else
795
 
        *resvec = run_single(*argv, *argv, calcsplash(0, 1, splashopt));
796
 
      goto finished;
797
 
    }
798
 
 
799
 
  prgs = (struct prg *)xcalloc(par, sizeof *prgs);
800
 
  gtimo_bufsize = par * PBUF_SIZE;
801
 
  gtimo_buf = (char *) calloc(gtimo_bufsize, sizeof(char));
802
 
  if (!gtimo_buf)
803
 
    gtimo_bufsize = 0;                          /* Accept error due memory shortage */
804
 
 
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))
809
 
    {
810
 
      perror("sigwinch sigaction");
811
 
      exit(1);
812
 
    }
813
 
 
814
 
  if (tcgetattr(0, &tio))
815
 
    {
816
 
      perror("tcgetattr");
817
 
      exit(1);
818
 
    }
819
 
  if (ioctl(0, TIOCGWINSZ, &wz) == 0)
820
 
    wzok = 1;
821
 
  if (wz.ws_row == 0) wz.ws_row = 24;
822
 
  if (wz.ws_col == 0) wz.ws_col = 80;
823
 
 
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);
828
 
 
829
 
  if (pipe(pidpipe))
830
 
    {
831
 
      perror("pipe");
832
 
      exit(1);
833
 
    }
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))
840
 
    {
841
 
      perror("sigchld sigaction");
842
 
      exit(1);
843
 
    }
844
 
 
845
 
  gettimeofday(&glastio, 0);
846
 
  limit = checklimit(inpar, isstart);
847
 
  lastlim = glastio;
848
 
  for (;;)
849
 
    {
850
 
      int active = 0;
851
 
      int maxfd = -1;
852
 
      int last = -1;
853
 
      pid_t pid = 0;
854
 
      int r = 0, s;
855
 
      long diff;
856
 
 
857
 
      gettimeofday(&now, 0);
858
 
      FD_ZERO(&rset);
859
 
      tv = now;
860
 
 
861
 
      if ((diff = timerdiff(now, lastlim)) >= 300 || diff < 0)
862
 
        {
863
 
#if DEBUG
864
 
          fprintf(stderr, "%d: doing checklimit after %ldms %ld\n", getpid(), diff, time(0));
865
 
#endif
866
 
          if ((limit = checklimit(inpar, isstart)) > argc)
867
 
            limit = argc;                       /* not more than the number of all scripts */
868
 
          lastlim = now;
869
 
          diff = 0;
870
 
        } 
871
 
#if DEBUG
872
 
      fprintf(stderr, "par=%d, inpar=%d, limit=%d (diff=%ld)\n", par, inpar, limit, diff);
873
 
#endif
874
 
      for (s = 0; s < par; s++)                 /* never leave this with break!! */
875
 
        {
876
 
        account:                                /* for the new process below */
877
 
          p = prgs + s;
878
 
          if (p == interactive_task)
879
 
            continue;                           /* don't count this here */
880
 
          if (p->fd || p->pid)
881
 
            active++;                           /* count all running procs */
882
 
          if (p->fd == 0)
883
 
            {
884
 
              if (interactive_task)
885
 
                continue;                       /* dont't start new processes */
886
 
              if (num >= argc)
887
 
                continue;                       /* nothing to do */
888
 
              if (p->pid == 0)
889
 
                {
890
 
                  if (active >= limit)
891
 
                    continue;                   /* load balancing */
892
 
                  if (run_mode)
893
 
                    {
894
 
                      if ((nodevec[num] = pickup_task()) == NULL)
895
 
                        continue;
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;
900
 
                    }
901
 
                  else {
902
 
                    p->name = *argv++;
903
 
                    p->arg0 = p->name;
904
 
                  }
905
 
                  p->splashadd = calcsplash(num, argc, splashopt);
906
 
                  p->num = num++;
907
 
                  if (interactive_task)
908
 
                    continue;                   /* don't start this here */
909
 
                  run(p);
910
 
                  if (p->pid == 0)
911
 
                    {
912
 
                      resvec[p->num] = 1;
913
 
                      if (run_mode)
914
 
                        finish_task(nodevec[p->num]);
915
 
                    }
916
 
                  gettimeofday(&now, 0);
917
 
                  tv = now;
918
 
                  goto account;                 /* take the new process into account */
919
 
                }
920
 
              continue;
921
 
            }
922
 
          FD_SET(p->fd, &rset);
923
 
          if (p->fd > maxfd)
924
 
            maxfd = p->fd;
925
 
          if (p->len == 0)
926
 
            continue;
927
 
          if ((last < 0) || timercmp(&tv,&p->lastio,>))
928
 
            {
929
 
              last = s;
930
 
              tv = p->lastio;
931
 
            }
932
 
        } /* for (s = 0; s < par; s++) */
933
 
 
934
 
      if (interactive_task)
935
 
        {
936
 
          if (active == 0)
937
 
            {
938
 
              p = interactive_task;
939
 
              resvec[p->num] = run_single(p->name, p->arg0, p->splashadd);
940
 
              if (run_mode)
941
 
                finish_task(nodevec[p->num]);
942
 
              p->pid = 0;
943
 
              p->fd = 0;
944
 
              interactive_task = NULL;
945
 
              continue;
946
 
            }
947
 
        }
948
 
 
949
 
      if (active == 0)
950
 
        {
951
 
          if (num < argc)
952
 
            fprintf(stderr, "ERROR: not all processed (%d of %d)\n", num, argc);
953
 
#if DEBUG
954
 
          if ((pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) > 0)
955
 
            fprintf(stderr, "ERROR: not all processes are checked\n");
956
 
#endif
957
 
          break;
958
 
        }
959
 
#if DEBUG
960
 
      fprintf(stderr, "active = %d\n", active);
961
 
#endif
962
 
      if (active == 1 && last >= 0)
963
 
        {
964
 
          p = prgs + last;
965
 
          if ((pid = waitpid(p->pid, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED)) == 0)
966
 
            {
967
 
              writebuf(p);
968
 
              continue;
969
 
            }
970
 
        }
971
 
 
972
 
      FD_SET(pidpipe[0], &rset);
973
 
      /* drain the pidpipe */
974
 
      while ((c = read(pidpipe[0], pipebuf, sizeof pipebuf)) > 0)
975
 
        ;
976
 
 
977
 
      if (pid == 0)
978
 
        pid = waitpid(-1, &r, (maxfd < 0 ? 0 : WNOHANG)|WUNTRACED);
979
 
 
980
 
      if (pid > 0)
981
 
        {
982
 
          if (pid == splashpid)
983
 
            splashpid = (pid_t)0;
984
 
          for (s = 0; s < par; s++)
985
 
            {
986
 
              p = prgs + s;
987
 
              if (p->pid == pid)
988
 
                {
989
 
                  if (WIFSTOPPED(r))
990
 
                    {
991
 
                      if (WSTOPSIG(r) == SIGTTIN)
992
 
                        {
993
 
                          pid_t pg = getpgid(pid);
994
 
                          if (pg > 0)
995
 
                            killpg(pg, SIGCONT);
996
 
                        }
997
 
                      continue;
998
 
                    }
999
 
                  callsplash(p->splashadd, p->name, arg);
1000
 
                  resvec[p->num] = WIFEXITED(r) ? WEXITSTATUS(r) : (WIFSIGNALED(r) ? 1 : 255);
1001
 
                  if (run_mode)
1002
 
                    finish_task(nodevec[p->num]);
1003
 
                  p->pid = 0;
1004
 
                  if (gtimo_running == p)
1005
 
                    {
1006
 
                      writebuf(p);
1007
 
                      if (p->fd)
1008
 
                        detach(p, GTIMO_OFFL);
1009
 
                      flushbuf();
1010
 
                      gtimo_running = 0;
1011
 
                    }
1012
 
                  else if (gtimo_running)
1013
 
                    {
1014
 
                      storebuf(p);
1015
 
                      if (p->fd)
1016
 
                        detach(p, GTIMO_USED);
1017
 
                    }
1018
 
                  else
1019
 
                    {
1020
 
                      writebuf(p);
1021
 
                      if (p->fd)
1022
 
                        detach(p, GTIMO_OFFL);
1023
 
                    }
1024
 
                  break;
1025
 
                }
1026
 
            } /* for (s = 0; s < par; s++) */
1027
 
          continue;
1028
 
        }
1029
 
 
1030
 
      if (maxfd < 0)
1031
 
        continue;                               /* start new processes? */
1032
 
 
1033
 
      if (timo >= 0)
1034
 
        tv.tv_sec += timo;
1035
 
 
1036
 
      isgtimo = 0;
1037
 
      if (gtimo >= 0 && !gtimo_running && last >= 0 && prgs[last].pid)
1038
 
        {
1039
 
          struct timeval gl = glastio;
1040
 
          gl.tv_sec += gtimo;
1041
 
          if ((timo < 0) || timercmp(&tv,&gl,>))
1042
 
            {
1043
 
              tv = glastio;
1044
 
              tv.tv_sec += gtimo;
1045
 
              isgtimo = 1;
1046
 
            }
1047
 
        }
1048
 
 
1049
 
      r = 0;
1050
 
      if (timo >= 0 || isgtimo)
1051
 
        {
1052
 
          int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1053
 
          struct timeval wait;
1054
 
 
1055
 
          timersub(&tv, &now, &wait);
1056
 
          if (wait.tv_usec < 0)
1057
 
            {
1058
 
              wait.tv_usec += 1000000;
1059
 
              wait.tv_sec--;
1060
 
            }
1061
 
          if (wait.tv_sec >= 0)
1062
 
            {
1063
 
              int check = limit < par && num < argc;
1064
 
 
1065
 
              if (check)                        /* shorten timeout for new limit and procs  ... */
1066
 
                {
1067
 
                  wait.tv_sec = 0;
1068
 
                  wait.tv_usec = (300 - diff) * 1000;
1069
 
                }
1070
 
#if DEBUG
1071
 
              fprintf(stderr, "going into select1 %d %ld %ld\n", last, wait.tv_sec, wait.tv_usec);
1072
 
#endif
1073
 
              r = select(setfd + 1, &rset, 0, 0, (last >= 0 || check) ? &wait : 0);
1074
 
 
1075
 
              if (check && (r == 0))            /* ... but do not throw out messages to early!!! */
1076
 
                continue;
1077
 
            }
1078
 
          else
1079
 
            {
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);
1083
 
            }
1084
 
        }
1085
 
      else
1086
 
        {
1087
 
          int setfd = (pidpipe[0] > maxfd) ? pidpipe[0] : maxfd;
1088
 
          r = select(setfd + 1, &rset, 0, 0, 0);
1089
 
        }
1090
 
 
1091
 
      if (r == -1)
1092
 
        {
1093
 
          if (errno == EINTR)
1094
 
            continue;
1095
 
          perror("select");
1096
 
          exit(1);
1097
 
        }
1098
 
      if (r == 0)
1099
 
        {
1100
 
          if (last < 0)         /* just in case... */
1101
 
            continue;
1102
 
          p = prgs + last;
1103
 
          writebuf(p);
1104
 
          if (isgtimo && p->pid)
1105
 
            gtimo_running = p;
1106
 
        }
1107
 
      else
1108
 
        {
1109
 
          for (s = 0; s < par; s++)
1110
 
            {
1111
 
              p = prgs + s;
1112
 
              if (p->fd == 0)
1113
 
                continue;
1114
 
              if (!FD_ISSET(p->fd, &rset))
1115
 
                continue;
1116
 
              r = read(p->fd, p->buf + p->len, sizeof(p->buf) - p->len);
1117
 
              if (r <= 0)
1118
 
                {
1119
 
                  if (!gtimo_running || p == gtimo_running)
1120
 
                    writebuf(p);
1121
 
                  close(p->fd);
1122
 
                  p->fd = 0;
1123
 
                  break;
1124
 
                }
1125
 
              p->len += r;
1126
 
              if (p->len == sizeof(p->buf))
1127
 
                {
1128
 
                  for (i = p->len - 1; i >= 0; i--)
1129
 
                    {
1130
 
                      if (p->buf[i] == '\n')
1131
 
                        break;
1132
 
                    }
1133
 
                  if (++i <= 0)
1134
 
                    i = p->len;
1135
 
                  p->len = i;
1136
 
                  writebuf(p);
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;
1141
 
                }
1142
 
              p->lastio = now;
1143
 
            } /* for (s = 0; s < par; s++) */
1144
 
        }
1145
 
    } /* for (;;) */
1146
 
 
1147
 
 finished:
1148
 
  waitsplash();
1149
 
  if (run_mode)
1150
 
    print_run_result(resvec, nodevec, run_mode);
1151
 
  else
1152
 
    {
1153
 
      for (i = 0; i < argc; i++)
1154
 
        {
1155
 
#if DEBUG
1156
 
          if (resvec[i] == 255)
1157
 
            {
1158
 
              fprintf(stderr, "ERROR: forgotten process??\n");
1159
 
              exit (1);
1160
 
            }
1161
 
#endif
1162
 
#if VERBOSE
1163
 
          printf(i ? " %d" : "%d", resvec[i]);
1164
 
#endif /* VERBOSE */
1165
 
        }
1166
 
#if VERBOSE
1167
 
      printf("\n");
1168
 
#endif /* VERBOSE */
1169
 
    }
1170
 
  return 0;
1171
 
}