/* Copyright (c) 2003, Rene Luria aka herel <rene@luria.ch>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* * this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* * notice, this list of conditions and the following disclaimer in the
* * documentation and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of its
* * contributors may be used to endorse or promote products derived from
* * this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. */
#define __USE_POSIX2
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#else
extern int getopt(int argc, char * const argv[], const char *optstring);
extern char *optarg;
extern int optind;
#endif
#include <unistd.h>
#define BUFSIZE 1024
#define LISTE "liste"
#define NUMPROCS 5
#define REPL "ARG"
typedef struct
{
int maxprocs;
char *filename;
int argc;
char **argv;
} *option_t;
typedef struct
{
int nb;
char **replace;
} *rep_t;
static void print_help (void)
{
#ifdef HAVE_GETOPTLONG
printf ("usage: threads [-c maxprocs|--procs=maxprocs] [-f filename|--file=filename] <command>\n");
#else
printf ("usage: threads [-c maxprocs] [-f filename] <command>\n");
#endif
}
static option_t parse_commandline (int argc, char **argv)
{
option_t options;
if ((options = malloc (sizeof *options)) == NULL)
{
perror ("malloc");
return NULL;
}
options->maxprocs = 0;
options->filename = NULL;
while (1)
{
int option;
#ifdef HAVE_GETOPTLONG
int longindex = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"file", 1, 0, 'f'},
{"procs", 1, 0, 'c'},
{0, 0, 0, 0}
};
if ((option = getopt_long (argc, argv, "hf:c:", long_options, &longindex)) == -1)
break;
#else
if ((option = getopt (argc, argv, "hf:c:")) == -1)
break;
#endif
switch (option)
{
case 'f':
if ((options->filename = malloc (strlen (optarg) + 1)) == NULL)
{
perror ("malloc");
return NULL;
}
strcpy (options->filename, optarg);
break;
case 'c':
options->maxprocs = atoi (optarg);
if (options->maxprocs == 0)
{
fprintf (stderr, "bad number of procs\n");
return NULL;
}
break;
case 'h':
print_help ();
return NULL;
break;
default:
fprintf (stderr, "bad argument\n");
return NULL;
}
}
if (options->filename == NULL)
{
if ((options->filename = malloc (strlen (LISTE) + 1)) == NULL)
{
perror ("malloc");
return NULL;
}
strcpy (options->filename, LISTE);
}
if (options->maxprocs == 0)
{
options->maxprocs = NUMPROCS;
}
if (argc - optind < 1)
{
fprintf (stderr, "Mauvais nombre d'arguments\n");
return NULL;
}
options->argc = argc - optind;
options->argv = argv + optind;
return options;
}
static char *get_path (char *commande)
{
char *path = getenv ("PATH");
char *cur2 = path, *cur1 = path;
char *complete = NULL;
if (strchr (commande, '/') != NULL) /* un path absolu */
{
struct stat buf;
if (stat (commande, &buf) == -1)
fprintf (stderr, "cannot stat %s\n", commande);
else
complete = commande;
}
else
{
while ((cur1 = strtok (cur2, ":")))
{
char *filename;
struct stat buf;
if (cur2)
cur2 = NULL;
if ((filename = malloc (strlen (cur1) + 1 + strlen (commande) + 1)) == NULL)
{
perror ("malloc");
break;
}
sprintf (filename, "%s/%s", cur1, commande);
if (stat (filename, &buf) == -1)
{
free (filename);
continue;
}
complete = filename;
break;
}
}
if (complete == NULL)
fprintf (stderr, "%s n'existe pas dans le path\n", commande);
return complete;
}
/* lance la commande d'un child */
static void do_commande (int argc, char **argv, char *replace)
{
int i;
for (i = 1; i < argc; i++)
{
char *cur;
if ((cur = strstr (argv[i], REPL)) != NULL)
{
char *newargv;
int taille = strlen (argv[i]) - strlen (REPL) + strlen (replace) + 1;
if ((newargv = malloc (taille)) == NULL)
{
perror ("malloc");
exit (EXIT_FAILURE);
}
memset (newargv, 0, taille);
strcpy (newargv, argv[i]); /* la première partie */
strncpy (newargv + (cur - argv[i]), replace, strlen (replace));
strcpy (newargv + (cur - argv[i]) + strlen (replace), cur + strlen (REPL));
argv[i] = newargv;
}
}
if (execv (argv[0], argv) == -1)
perror ("execv");
exit (EXIT_SUCCESS);
}
/* launch les process pour en garder en permanence maxprocs */
static int launch_processes (int maxprocs, rep_t prep, int argc, char **argv)
{
int ret = 0;
int running = 0, i = 0, status;
if (prep == NULL)
return -1;
while (running || !i)
{
int pid;
if ((running == maxprocs) || /* soit ya déjà MAXPROCS qui tournent */
(i == prep->nb)) /* soit tous les process ont été launchés */
{
wait (&status);
running--;
}
if (i < prep->nb)
{
if ((pid = fork ()) == -1)
{
perror ("fork");
ret = 1;
break;
}
if (pid > 0) /* parent */
{
running++;
i++;
}
else /* fils */
{
do_commande (argc, argv, prep->replace[i]);
exit (EXIT_SUCCESS); /* doit pas revenir mais on sait jamais */
}
}
}
return ret;
}
/* lit un fichier et retourne une struct avec chaque ligne */
static rep_t get_replace (char *filename)
{
FILE *fp;
rep_t prep = NULL;
char **replace = NULL;
int nb = 0;
if ((fp = fopen (filename, "r")) == NULL)
perror ("fopen");
else
{
char buffer[BUFSIZE];
while (fgets (buffer, BUFSIZE, fp) != NULL)
{
if (buffer[strlen (buffer) - 1] == '\n') /* nique la fin de ligne */
buffer[strlen (buffer) - 1] = '\0';
if (nb % 10 == 0)
{
if ((replace = realloc (replace, (nb + 10) * (sizeof *replace))) == NULL)
{
perror ("realloc");
break;
}
}
if ((replace[nb] = malloc (strlen (buffer) + 1)) == NULL)
{
perror ("malloc");
break;
}
strcpy (replace[nb], buffer);
nb++;
}
}
if (replace)
{
if ((prep = malloc (sizeof *prep)) == NULL)
perror ("malloc");
else
{
prep->nb = nb;
prep->replace = replace;
}
}
return prep;
}
int main (int argc, char **argv)
{
rep_t prep;
option_t options;
if ((options = parse_commandline (argc, argv)) == NULL)
return EXIT_FAILURE;
if ((options->argv[0] = get_path (options->argv[0])) == NULL)
return EXIT_FAILURE;
if ((prep = get_replace (options->filename)) == NULL)
return EXIT_FAILURE;
if (launch_processes (options->maxprocs, prep, options->argc, options->argv))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}