Avvolgere un processo shell per manipolare un file system, come migliorare la progettazione?

0

Nella mia libreria API (C ++) sto avvolgendo un processo shell (generato con popen ) con diverse classi manipolare un file system.

La classe base ProcessFileWrap avvolge le attività generali del filesystem ed esegue altre attività personalizzate dalle sue classi derivate. Sembra di seguito in pseudocodice:

// ProcessFileWrap base class methods

ExecCustomProcess(string process) // does popen(process, "r")

ChangeDir(string path) // changes the current directory

AddFolder(string path) // wrapper for mkdir

AddFile(string path) // wrapper for touch

MakeLink(string pathToFile, string pathToLink) // wrapper for ln -s

// ... And so on

Esistono diverse classi derivate. Uno di questi è GitFileWrap , che esegue il wrapping dei processi git in questo modo (di nuovo in psuedocode). È necessario eseguire alcune attività specifiche del file system dopo o durante il processo:

// GitFileWrap derived class methods

// Calls ChangeDir(pathToRepo) and ExecCustomProcess("git pull")
// Afterwards it creates links for each pulled file in a custom 
// directory via MakeLink(file, customLoc).
Pull()

// Calls ExecCustomProcess(git commit -m obj.GetCommitMessage())
// A GitObject represents a git versioned object in the filesystem.
Commit(GitObject obj)

// ... And so on

Quindi gli utenti dell'API chiamerebbero i metodi dalle classi derivate, se necessario.

Sono consapevole del fatto che i processi di wrapping non sono in generale di best practice, quindi se puoi, per favore commenta il design con l'ipotesi che non ci sia davvero un'altra alternativa (o immagino che potresti, ma apprezzerei i commenti su il disegno sopra). Grazie!

Nota: non è possibile per me utilizzare libgit2

    
posta ifma 29.04.2017 - 06:25
fonte

2 risposte

2
  1. Non utilizzare processi esterni se puoi aiutarlo. Utilizza direttamente le chiamate di sistema: chdir(path) , mkdir(pathname, mode) , std::fstream o open(path, O_CREAT) anziché touch , symlink(target, path) anziché ln -s . In un programma C ++, non ci sono buoni motivi per usare i binari del wrapper invece di chiamare direttamente queste syscalls.
  2. Quando devi veramente usare comandi esterni, non usare popen() . popen() è lento e irrimediabilmente insicuro perché avvia sempre una shell e quindi interpreta inutilmente glob e variabili di shell. Il modo sicuro per avviare un sottoprocesso è con fork () ed execve () o posix_spawn () . Queste funzioni passano i file e i parametri del programma come una matrice invece di richiedere l'interpolazione e l'escape degli argomenti in stringhe, solo per fare in modo che un interprete della shell provi ad analizzare gli argomenti del comando per capire come dividere le stringhe che alla fine passa esegui comunque.
risposta data 29.04.2017 - 16:09
fonte
2

Problemi con il disegno sopra:

  1. Sarà lento, a causa della necessità di generare un nuovo processo per ogni operazione e attendere che finisca.
  2. Se provi a risolvere il problema, non aspettando che i processi generati finiscano sarà vulnerabile alle condizioni di gara, ad es. genera un'attività che crea una directory & quindi uno per toccare i file in quella directory rischia che la directory non esista ancora.
  3. Sarà un incubo per la sicurezza - a meno che non si eseguano controlli approfonditi c'è il strong rischio che individui malintenzionati o impazienti facciano uso del fatto che si sta generando, ad es. ChangeDir("/ && rm -rf .") e molte varianti.
  4. Gli errori saranno indicati dal codice di ritorno, ma per maggiori dettagli su cosa è stato sbagliato dovrai analizzare gli stdout: e / o stderr: di stream.

Di conseguenza il mio commento sul design che è dettagliato nella domanda è un clamoroso non pensate nemmeno di farlo in quel modo! - Se non c'è letteralmente altro altra scelta - vedi sotto per le alternative - dovrai quindi dedicare molto tempo e impegno all'analisi dei valori di input per assicurarti che siano sicuri e resterà ancora essere problemi.

Possibili soluzioni

  1. Esistono già chiamate alla libreria POSIX C & / o C ++ per eseguire quasi tutte le operazioni del sistema operativo sopra elencate, la maggior parte delle quali ha codici di errore chiari per qualsiasi problema. per esempio. #include <unistd.h> ti dà accesso a: int chdir(const char *path);

  2. Per le operazioni git utilizzare una libreria git come libgit2

Fondamentalmente, devi solo dedicare un po 'di tempo a leggere la documentazione e / oa cercare sul Web per essere in grado di fare tutto ciò che descrivi in modo sicuro, veloce e compatto.

Se non riesci a utilizzare le librerie per utilità specifiche come git, non ci sono ancora scuse per usare spawn per cose che sono nel C & generico; Librerie C ++.

Per mitigare la velocità, la razza e i problemi di sicurezza consiglio di raccogliere (e controllare accuratamente) argomenti come i messaggi di commit e la creazione di uno script di shell temporaneo, in modo da poter avviare una singola shell eseguendo la sequenza di comandi , per esempio per git. Puoi anche guardare al chaing dei comandi git in tali script.

    
risposta data 29.04.2017 - 08:31
fonte

Leggi altre domande sui tag