/* This file is part of vdr-filebrowser. vdr-filebrowser is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. vdr-filebrowser is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar. If not, see . */ #include #include #include #include #include #include "threads.h" #include "commands.h" /* Implementation cCommandThread */ cCommandThread::cCommandThread(cFilebrowserStatebag* Statebag, char* DestinationFile, char* CurrentFile) : cThread() { Description=NULL; this->DestinationFile=DestinationFile ? strdup(DestinationFile) : NULL; this->CurrentFile=CurrentFile ? strdup(CurrentFile) : NULL; this->Statebag=Statebag; //deep copy of Selected files SelectedFiles=new cStringContainerList(); for(int i=0; iGetSelectedFiles()->Count(); i++) { SelectedFiles->Add(new cStringContainer(strdup((char*)Statebag->GetSelectedFiles()->Get(i)->GetObject()))); } State=tsCreated; RemoveRequested=false; UserInput=Statebag->UserInput; UserInputPassword=Statebag->UserInputPassword; } cCommandThread::~cCommandThread() { if(Description) free (Description); if(DestinationFile) free (DestinationFile); if(CurrentFile) free (CurrentFile); if(SelectedFiles) free(SelectedFiles); } cOsdMenu* cCommandThread::GetMenu() { return NULL; } void cCommandThread::Cancel(int WaitSeconds) { cThread::Cancel(WaitSeconds); State=tsFinished; } void cCommandThread::Pause() { if(Running()) { State=tsPaused; } } void cCommandThread::Resume() { if(Running()) { State=tsRunning; } } /* Implementation cCommandThreadTextOutput */ cCommandThreadTextOutput::cCommandThreadTextOutput(cFilebrowserStatebag* Statebag, char* DestinationFile, char* CurrentFile) : cCommandThread(Statebag, DestinationFile, CurrentFile) { OutputBufferLength=128; OutputBuffer=(char*)malloc(OutputBufferLength); memset(OutputBuffer, 0, OutputBufferLength); ChildPid=-1; } void cCommandThreadTextOutput::Action() { State=tsRunning; D(fprintf(stderr, "starting thread\n")); int child_output[2]; pipe(child_output); pid_t pid=ChildPid=fork(); if(pid == 0) //child { setpgid(ChildPid, 0); int MaxPossibleFileDescriptors = getdtablesize(); for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++) { if(/*i!=child_output[0] &&*/ i!=child_output[1]) { close(i); //close all dup'ed filedescriptors } } // close(child_output[0]); D(fprintf(stderr, "fork succeeded, now redirecting output\n")); dup2(child_output[1], STDOUT_FILENO); dup2(child_output[1], STDERR_FILENO); int dev_null=open("/dev/null", O_RDWR, 0); dup2(dev_null, STDIN_FILENO); close(dev_null); Execute(); // close(child_output[1]); return; } else if (pid < 0) { D(fprintf(stderr, "fork failed\n")); return; } else // parent { D(fprintf(stderr, "reading output of child process\n")); FILE* out; out=fdopen(child_output[0], "r"); D(fprintf(stderr, "opening output succeeded\n")); close(child_output[1]); if(out) { char* caret=OutputBuffer; char c; while((c=fgetc(out)) != EOF && caret - OutputBuffer < OutputBufferLength) { switch(c) { case '\b': if(caret > OutputBuffer) { caret--; } break; case '\r': case '\f': case '\v': for(char* i=caret; i>=OutputBuffer; i--) { if(i==OutputBuffer) { caret=i; break; } if(*i=='\n') { caret=i+1; break; } } break; default: *(caret++)=c; break; } if(caret - OutputBuffer >= OutputBufferLength - 2) { int Offset=caret - OutputBuffer; OutputBufferLength*=2; Lock(); char* TmpBuffer=(char*)realloc(OutputBuffer, OutputBufferLength); if(TmpBuffer && OutputBufferLength < 1024*1024) { OutputBuffer=TmpBuffer; caret=OutputBuffer + Offset; memset(caret, 0, OutputBufferLength - Offset); D(fprintf(stderr, "resized OutputBuffer to %d bytes\nCaret at Position %d is \"%s\"\n", (int)OutputBufferLength, Offset, caret)); } else { D(fprintf(stderr, "couldn't resize output buffer\n")); OutputBuffer=TmpBuffer ? TmpBuffer : OutputBuffer; caret=OutputBuffer; OutputBufferLength/=2; memset(caret, 0, OutputBufferLength); //TODO: Error message } Unlock(); } } int ChildStatus; D(fprintf(stderr, "Waiting for child %d\n", ChildPid)); waitpid(ChildPid, &ChildStatus, 0); RemoveRequested&=!ChildStatus; ChildPid=-1; if((long)strlen(OutputBuffer) == OutputBufferLength) { D(fprintf(stderr, "buffer ran full (%d)\n%s\n", strlen(OutputBuffer), OutputBuffer)); while((c=fgetc(out)) != EOF) {}; } D(fprintf(stderr, "forked thread closed, output was:\n%s\n", OutputBuffer)); fclose(out); Statebag->UpdateRequested=true; } else { D(fprintf(stderr, "failed to read output of Execute\n")); } D(fprintf(stderr, "Closing pipe\n")); close(child_output[0]); State=tsFinished; } } cCommandThreadTextOutput::~cCommandThreadTextOutput() { D(fprintf(stderr, "CommandThreadTextOutput: Destructor called\n")); Cancel(); free(OutputBuffer); } void cCommandThreadTextOutput::Cancel(int WaitSeconds) { D(fprintf(stderr, "CommandThreadTextOutput: Cancel called")); if(ChildPid > 1) { D(fprintf(stderr, " - killing %d", ChildPid)); kill(-ChildPid, SIGTERM); ChildPid=-1; } D(fprintf(stderr, "\n")); cCommandThread::Cancel(WaitSeconds); } cOsdMenu* cCommandThreadTextOutput::GetMenu() { cOsdMenuTextOutput* OutputMenu=new cOsdMenuTextOutput(this); OutputMenu->SetText(OutputBuffer); return OutputMenu; } void cCommandThreadTextOutput::Pause() { if(Running() && ChildPid > 1) { D(fprintf(stderr, "Sending sigstop to %d\n", ChildPid)); kill(-ChildPid, SIGSTOP); } cCommandThread::Pause(); } void cCommandThreadTextOutput::Resume() { if(Running() && ChildPid > 1) { D(fprintf(stderr, "Sending sigcont to %d\n", ChildPid)); kill(-ChildPid, SIGCONT); } cCommandThread::Resume(); } /* Implementation cConfigCommandThread */ cConfigCommandThread::cConfigCommandThread(cFilebrowserStatebag* Statebag, char* DestinationFile, char* CurrentFile, cFilebrowserConfigCommand* Command) : cCommandThreadTextOutput(Statebag, DestinationFile, CurrentFile) { this->Command=Command; if(Command->UsesCurrentFile()) { Description=(char*)malloc(strlen(Command->GetName()) + strlen(CurrentFile) + 2); sprintf(Description, "%s%s%s", Command->GetName(), strcmp("", CurrentFile) == 0 ? "" : " ", strcmp("", CurrentFile) == 0 ? "" : CurrentFile); } else if(Command->UsesDestination()) { Description=(char*)malloc(strlen(Command->GetName()) + strlen(DestinationFile) + 2 + strlen(tr(" to "))); sprintf(Description, "%s%s%s", Command->GetName(), strcmp("", DestinationFile) == 0 ? "" : tr(" to "), strcmp("", DestinationFile) == 0 ? "" : DestinationFile); } else { Description=(char*)malloc(strlen(Command->GetName()) + 1); sprintf(Description, "%s", Command->GetName()); } SetDescription(Description); RemoveRequested=Command->RemoveWhenFinished(); // D(fprintf(stderr, "got %d marked files\n", Statebag->GetSelectedFiles()->Count())); D(fprintf(stderr, "created thread %s\n", Description)); } cOsdMenu* cConfigCommandThread::GetMenu() { cOsdMenuTextOutput* OutputMenu=(cOsdMenuTextOutput*)cCommandThreadTextOutput::GetMenu(); OutputMenu->SetTitle(Description); return OutputMenu; } char* cConfigCommandThread::SelectedFilesHandler(const char* OrgString, const char* CurrentPos, const cCommandParser::cHandlerParameters* Params) { cStringContainerList* SelectedFiles=(cStringContainerList*)Params->Data; char* Text=NULL; int TextLength=0; D(fprintf(stderr, "adding marked files - have %d\n", SelectedFiles->Count())); //expand filenames char* ifs=getenv("IFS"); D(fprintf(stderr, "using IFS=\"%s\"\n", ifs)); for(int i=0; iCount(); i++) { int FileNameLength; char* File=cCommandParser::EscapeShellArgument((char*)SelectedFiles->Get(i)->GetObject(), &FileNameLength); D(fprintf(stderr, "adding file %s\n", File)); FileNameLength++; /*\0 or IFS[0] */ Text=(char*)realloc(Text, TextLength + FileNameLength); sprintf(Text + TextLength - (TextLength > 0 ? 1 : 0), "%.1s%s", i > 0 ? (ifs ? ifs : " ") : "", File); TextLength+=FileNameLength; free(File); } D(fprintf(stderr, "marked files: %s (length: %d)\n", Text, TextLength)); return Text; } int cConfigCommandThread::Execute() { D(fprintf(stderr, "parsing %s\n", Command->GetCommand())); cCommandParser CommandParser(Command->GetCommand()); CommandParser.AddReplacement('f', CurrentFile); CommandParser.AddReplacement('d', DestinationFile); CommandParser.AddReplacement('D', DestinationFile); CommandParser.AddHandler('m', &cConfigCommandThread::SelectedFilesHandler, SelectedFiles); CommandParser.AddHandler('M', &cConfigCommandThread::SelectedFilesHandler, SelectedFiles); CommandParser.AddReplacement('i', UserInput); CommandParser.AddReplacement('p', UserInputPassword); cString Text=CommandParser.Parse(); D(fprintf(stderr, "executing %s\n", *Text)); return execl("/bin/sh", "sh", "-c", *Text, NULL); }