[geeks] What is a good download manager for Linux
der Mouse
mouse at Rodents.Montreal.QC.CA
Wed May 7 14:48:43 CDT 2008
> Since the question's been asked, I'd actually like to know if anyone
> has recommendations for a CLI or GUI download manager that handles
> most of the stuff that wget or curl do, but adds multi-threading
> downloads.
% wget url1 &
% wget url2 &
% wget url3 &
Rudimentary but very simple.
If you have a long list and want to do soemthing like keep three
downloads going at all times, you'll need a little extra software. I
have a program I wrote for the purpose, which might serve as a starting
point if naught else; I'll include it below. (Warning: it's written in
gcc, not C.)
/~\ The ASCII der Mouse
\ / Ribbon Campaign
X Against HTML mouse at rodents.montreal.qc.ca
/ \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B
(This could be used with something like
% < file-of-URLs sed -e 's/^/wget /' | multi 4
to run 4 parallel wgets.)
/*
* multi - run multiple commands in parallel
*
* Usage: multi [-v] [-f] [-redo] [-sh shell] N
*
* Reads commands from stdin. Runs them by passing them to the shell
* specified with -sh (default to $SHELL) using the -c convention, but
* runs N at a time, starting a new one when one exits. Any death
* other than exit(0) is noted on stderr. All commands inherit
* multi's stdout, stderr, and process group; they get /dev/null on
* stdin. With -v, as each command is started, it's printed (without
* commentary) on stderr. -redo says that any command that terminates
* abnormally should be rerun. -f is like -v, except that it logs
* commands as they finish. (With both -f and -v, commands are logged
* twice, first as they start and again as they finish; there is no
* indication given whether a given line corresponds to a start or a
* finish.)
*
* multi does nothing with most signals. It does catch SIGINFO, and on
* receipt of it, prints out a list of commands presently running.
*
* If stdin does not end with a newline, one is silently supplied.
*
* multi's own exit status is a count of errorful child terminations,
* or 1 if some other error caused a premature exit - but never more
* than 127 in any case.
*/
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <strings.h>
#include <sys/wait.h>
extern const char *__progname;
static const char *devnull = "/dev/null";
static int v_flag = 0;
static int f_flag = 0;
static int redo_flag = 0;
static int dnfd;
static const char *shell;
static int N;
static pid_t *kids;
static char **cmds;
static int nkids;
static int ateof;
static int nerr;
static volatile int wantinfo;
static void usage(void) __attribute__((__noreturn__));
static void usage(void)
{
fprintf(stderr,"Usage: %s [-v] [-redo] [-sh shell] N\n",__progname);
exit(1);
}
static void runcmd(char *cmd)
{
pid_t kid;
int xp[2];
int err;
int r;
cmds[nkids] = cmd;
if (pipe(xp) < 0)
{ fprintf(stderr,"%s: pipe: %s\n",__progname,strerror(errno));
exit(1);
}
kid = fork();
if (kid < 0)
{ fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno));
exit(1);
}
if (kid == 0)
{ if (dnfd != 0)
{ dup2(dnfd,0);
close(dnfd);
}
close(xp[0]);
fcntl(xp[1],F_SETFD,1);
execl(shell,shell,"-c",cmd,(char *)0);
err = errno;
write(xp[1],&err,sizeof(int));
exit(0);
}
close(xp[1]);
r = 0;
while (r < sizeof(int))
{ int n;
n = read(xp[0],((char *)&err)+r,sizeof(int)-r);
if (n < 0)
{ if (errno == EINTR) continue;
fprintf(stderr,"%s: exec pipe read error: %s\n",__progname,strerror(errno));
exit(1);
}
if (n == 0)
{ close(xp[0]);
if (r == 0)
{ kids[nkids] = kid;
nkids ++;
if (v_flag) fprintf(stderr,"%s\n",cmd);
return;
}
fprintf(stderr,"%s: exec pipe protocol failure\n",__progname);
exit(1);
}
r += n;
}
fprintf(stderr,"%s: exec %s: %s\n",__progname,cmd,strerror(err));
free(cmd);
close(xp[0]);
}
static void print_exit_status(FILE *f, int status)
{
if (WIFEXITED(status))
{ fprintf(f,"exit %d",WEXITSTATUS(status));
}
else if (WIFSIGNALED(status))
{ fprintf(f,"signal %d [%s]%s",WTERMSIG(status),strsignal(WTERMSIG(status)),WCOREDUMP(status)?" (core dumped)":"");
}
else if (WIFSTOPPED(status))
{ fprintf(f,"stopped [%d %s]",WSTOPSIG(status),strsignal(WSTOPSIG(status)));
}
else
{ fprintf(f,"undecodable %d",status);
}
}
static void deadkid(pid_t pid, int status)
{
int i;
char *cmd;
for (i=0;i<nkids;i++)
{ if (pid == kids[i])
{ nkids --;
cmd = cmds[i];
if (i < nkids)
{ cmds[i] = cmds[nkids];
kids[i] = kids[nkids];
}
if (f_flag) fprintf(stderr,"%s\n",cmd);
if (!WIFEXITED(status) || WEXITSTATUS(status))
{ fprintf(stderr,"%s: %s: ",__progname,cmd);
print_exit_status(stderr,status);
if (redo_flag) fprintf(stderr," (retrying)");
fprintf(stderr,"\n");
if (redo_flag)
{ runcmd(cmd);
cmd = 0;
}
nerr ++;
}
free(cmd);
return;
}
}
fprintf(stderr,"%s: unknown child %d died (",__progname,(int)pid);
print_exit_status(stderr,status);
fprintf(stderr,")\n");
}
static char *iline(void)
{
char *b;
int l;
int a;
int c;
static void addc(char ch)
{ if (l >= a) b = realloc(b,a=l+16);
b[l++] = ch;
}
b = 0;
l = 0;
a = 0;
while (1)
{ c = getchar();
if (c == EOF)
{ if (l > 0) addc('\0');
return(b);
}
if (c == '\n')
{ addc('\0');
return(b);
}
addc(c);
}
}
static void caught_info(int sig __attribute__((__unused__)))
{
wantinfo = 1;
}
static void catch_info(void)
{
struct sigaction sa;
sa.sa_handler = caught_info;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINFO,&sa,0);
wantinfo = 0;
}
static void printinfo(void)
{
static int dev_tty_fd = -1;
FILE *f;
int i;
static int w(void *cookie __attribute__((__unused__)), const char *buf, int len)
{ return(write(dev_tty_fd,buf,len));
}
if (dev_tty_fd < 0)
{ if (dev_tty_fd == -1)
{ dev_tty_fd = open("/dev/tty",O_WRONLY,0);
if (dev_tty_fd < 0) dev_tty_fd = -2;
}
if (dev_tty_fd < 0) return;
}
f = fwopen(0,w);
for (i=0;i<nkids;i++) fprintf(f,"(%d) %s\n",(int)kids[i],cmds[i]);
fclose(f);
}
int main(int, char **);
int main(int ac, char **av)
{
shell = getenv("SHELL");
ac --;
av ++;
while (1)
{ if (!strcmp(av[0],"-v"))
{ v_flag = 1;
ac --;
av ++;
continue;
}
if (!strcmp(av[0],"-f"))
{ f_flag = 1;
ac --;
av ++;
continue;
}
if (!strcmp(av[0],"-redo"))
{ redo_flag = 1;
ac --;
av ++;
continue;
}
if (!strcmp(av[0],"-sh"))
{ if (!av[1]) usage();
shell = av[1];
ac -= 2;
av += 2;
continue;
}
break;
}
if (ac != 1) usage();
N = atoi(av[0]);
if (N < 1) usage();
dnfd = open(devnull,O_RDONLY,0);
if (dnfd < 0)
{ fprintf(stderr,"%s: %s: %s\n",__progname,devnull,strerror(errno));
exit(1);
}
nkids = 0;
ateof = 0;
nerr = 0;
kids = malloc(N*sizeof(pid_t));
cmds = malloc(N*sizeof(char *));
catch_info();
while (nkids || !ateof)
{ if (wantinfo)
{ wantinfo = 0;
printinfo();
}
if ((nkids < N) && !ateof)
{ char *cmd;
cmd = iline();
if (cmd == 0)
{ ateof = 1;
continue;
}
runcmd(cmd);
}
else
{ pid_t dead;
int status;
dead = wait(&status);
if (dead < 0)
{ if (errno == EINTR) continue;
fprintf(stderr,"%s: wait: %s\n",__progname,strerror(errno));
exit(1);
}
deadkid(dead,status);
}
}
exit((nerr>127)?127:nerr);
}
More information about the geeks
mailing list