Cwgethelper - práce s programem wget z C++

Petr Wiedemann, 15. březen 2009

Potřeboval jsem napsat program, který bude stahovat stránky a hledat v nich informace. Když jsem přemýšlel o tom, jak zajistit stažení požadované stránky a její uložení do souboru, jako nejjednodušší řešení mi připadalo použít program wget. Je to vynikající nástroj pro neinteraktivní stahování obsahu z webu.

Pro moje účely bylo nutné spouštět wget i s parametrem, kam má obsah stránky ukládat, abych v něm mohl později hledat. wget -q -Oindex.html http://www.pdynet.net

  • parametr q určuje, že wget nebude vypisovat informace o průběhu stahování
  • parametr O říká, kam uložit obsah požadované stránky
  • poslední parametr je požadovaná adresa

OK, věděl jsem tedy, jak chci volat wget. Jenže jak to zajistit z programu, který je napsán v C++? Například funkcí execvp(). Funkce z rodiny exec() spustí požadovaný program a to tak, že volající proces nahradí volaným. Funkce execvp() má 2 parametry, kde 1. je jméno programu, který se má spustit a 2. je seznam parametrů, které mu budou předány. Poslední v seznamu parametrů musí být NULL. První v seznamu musí být jméno volaného programu. Pro tento program to potom bude argv[0].

Použití je tedy následující:

char *arg_list[5];
arg_list[0] = "wget";
arg_list[1] = "-q";
arg_list[2] = "-Oindex.html";
arg_list[3] = "http://www.pdynet.net";
arg_list[4] = NULL;

execvp("wget", arg_list);

Jenže je tady další problém. Funkce execvp() nahradí původní proces tím, který spouští. Proto se před jejím voláním musí udělat kopie hlavního procesu a z této kopie zavolat funkci execvp(). To zajistí funkce fork(). Funkce fork() vytvoří kopii procesu, ze kterého je volána. Program (rodič i potomek) pokračuje od místa volání. Rozdíl je pouze v návratové hodnotě z funkce fork(). Procesu rodiče vrátí PID potomka, procesu potomka vrací 0.

pid_t child_pid = fork();
if (child_pid != 0)
  cout << "Tohle vypisuje rodic." << endl;
else
  cout << "Tohle vypisuje potomek." << endl;

Poslední věc je synchronizace procesů rodiče a potomka. Po vytvoření nového procesu, musí rodič počkat, než proces potomka skončí. Čekání na dokončení procesu má za úkol funkce waitpid(). PID potomka vrací funkce fork(), takže rodič ví, na který proces má čekat.

pid_t child_pid = fork();
if (child_pid != 0)
{
  int waitstatus;
  cout << "Cekam na dokonceni procesu " << child_pid << endl;
  waitpid(child_pid, &waitstatus, 0);
  cout << "OK, je po nem" << endl;
}

A aby byl život jednodušší, vznikla třída Cwgethelper, která má za úkol spustit wget s parametry, co má kam uložit a počkat, než wget skončí.

Příklad použití

Cwgethelper *pwgethelper;
pwgethelper = new Cwgethelper;
pwgethelper->download("index.html", "http://www.pdynet.net");
delete pwgethelper;

Pracující inteligence je ve třídě zastoupená funkcemi runwget() a download().

pid_t Cwgethelper::runwget(char **arg_list)
{
  child_pid = fork();
  if (child_pid != 0)
    return(child_pid);
  else
  {
    execvp(WGET, arg_list);
    cerr << "Chyba pri volani funkce execvp()" << endl;
    abort();
  }
}

bool Cwgethelper::download(const char *saveto, const char *url)
{
  strncpy(argv2, "-O", MAX_BUFFER_SIZE - 1);
  strncat(argv2, saveto, MAX_BUFFER_SIZE - 3);
  strncpy(argv3, url, MAX_BUFFER_SIZE - 1);
  argv2[MAX_BUFFER_SIZE - 1] = 0;
  argv3[MAX_BUFFER_SIZE - 1] = 0;

  char *arg_list[5];
  arg_list[0] = argv0;
  arg_list[1] = argv1;
  arg_list[2] = argv2;
  arg_list[3] = argv3;
  arg_list[4] = argv4;

  unlink(saveto);
  runwget(arg_list);

  while (waitpid(child_pid, &waitstatus, 0) < 0)
  {
    cerr << "chyba : waitpid " << child_pid << endl;
    if (errno != EINTR)
    {
      waitstatus = -1;
      return false;
    }
  }

  return true;
}

Zdrojové soubory třídy: