sbProcess.cpp
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4 //
5 // BEGIN SONGBIRD GPL
6 //
7 // This file is part of the Songbird web player.
8 //
9 // Copyright(c) 2005-2009 POTI, Inc.
10 // http://songbirdnest.com
11 //
12 // This file may be licensed under the terms of of the
13 // GNU General Public License Version 2 (the "GPL").
14 //
15 // Software distributed under the License is distributed
16 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
17 // express or implied. See the GPL for the specific language
18 // governing rights and limitations.
19 //
20 // You should have received a copy of the GPL along with this
21 // program. If not, go to http://www.gnu.org/licenses/gpl.html
22 // or write to the Free Software Foundation, Inc.,
23 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 //
25 // END SONGBIRD GPL
26 //
27 */
28 
29 //------------------------------------------------------------------------------
30 //------------------------------------------------------------------------------
31 //
32 // Songbird process services.
33 //
34 //------------------------------------------------------------------------------
35 //------------------------------------------------------------------------------
36 
42 //------------------------------------------------------------------------------
43 //
44 // Songbird process imported services.
45 //
46 //------------------------------------------------------------------------------
47 
48 // Self imports.
49 #include "sbProcess.h"
50 
51 // Mozilla imports.
52 #include <nsAutoLock.h>
53 #include <nsAutoPtr.h>
54 #include <nsThreadUtils.h>
55 
56 
57 //------------------------------------------------------------------------------
58 //------------------------------------------------------------------------------
59 //
60 // Songbird process class.
61 //
62 //------------------------------------------------------------------------------
63 //------------------------------------------------------------------------------
64 
65 //------------------------------------------------------------------------------
66 //
67 // Songbird process nsISupports implementation.
68 //
69 //------------------------------------------------------------------------------
70 
72 
73 
74 //------------------------------------------------------------------------------
75 //
76 // Public process services.
77 //
78 //------------------------------------------------------------------------------
79 
80 
86 /* static */ nsresult
87 sbProcess::New(sbProcess** aProcess)
88 {
89  // Validate arguments.
90  NS_ENSURE_ARG_POINTER(aProcess);
91 
92  // Create a new process object.
93  nsRefPtr<sbProcess> process = new sbProcess();
94  NS_ENSURE_TRUE(process, NS_ERROR_OUT_OF_MEMORY);
95 
96  // Create the process lock.
97  process->mProcessLock = nsAutoLock::NewLock("sbProcess::mProcessLock");
98  NS_ENSURE_TRUE(process->mProcessLock, NS_ERROR_OUT_OF_MEMORY);
99 
100  // Return results.
101  process.forget(aProcess);
102 
103  return NS_OK;
104 }
105 
106 
111 nsresult
113 {
114  PRStatus status;
115  nsresult rv;
116 
117  // Set up to auto-kill process.
118  sbAutoKillProcess autoSelf(this);
119 
120  // Operate under the process lock.
121  {
122  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
123  nsAutoLock autoProcessLock(mProcessLock);
124 
125  // Get the number of arguments.
126  PRUint32 argCount = mArgList.Length();
127  NS_ENSURE_TRUE(argCount > 0, NS_ERROR_ILLEGAL_VALUE);
128 
129  // Convert the UTF-16 argument list to a UTF-8 argument list.
130  nsTArray<nsCString> argListUTF8;
131  for (PRUint32 i = 0; i < argCount; i++) {
132  NS_ENSURE_TRUE(argListUTF8.AppendElement
133  (NS_ConvertUTF16toUTF8(mArgList[i])),
134  NS_ERROR_OUT_OF_MEMORY);
135  }
136 
137  // Allocate a null-terminated char* argument list and set it up for
138  // auto-disposal.
139  char** argList = reinterpret_cast<char**>
140  (NS_Alloc((argCount + 1) * sizeof(char*)));
141  NS_ENSURE_TRUE(argList, NS_ERROR_OUT_OF_MEMORY);
142  sbAutoNSTypePtr<char*> autoArgList(argList);
143 
144  // Convert the argument list to a null-terminated char* argument list.
145  for (PRUint32 i = 0; i < argCount; i++) {
146  argList[i] = const_cast<char*>(argListUTF8[i].get());
147  }
148  argList[argCount] = NULL;
149 
150  // Set up the process attributes and set them up for auto-disposal.
151  PRProcessAttr* processAttr = PR_NewProcessAttr();
152  NS_ENSURE_TRUE(processAttr, NS_ERROR_FAILURE);
153  sbAutoPRProcessAttr autoProcessAttr(processAttr);
154 
155  // Set up process stdin.
156  if (mPipeStdinString) {
157  // Create a process stdin pipe and set it up for auto-disposal.
158  PRFileDesc* stdinReadFD;
159  PRFileDesc* stdinWriteFD;
160  status = PR_CreatePipe(&stdinReadFD, &stdinWriteFD);
161  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
162  sbAutoPRFileDesc autoStdinReadFD(stdinReadFD);
163  sbAutoPRFileDesc autoStdinWriteFD(stdinWriteFD);
164 
165  // Set up stdin pipe file descriptors.
166  status = PR_SetFDInheritable(stdinReadFD, PR_TRUE);
167  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
168  status = PR_SetFDInheritable(stdinWriteFD, PR_FALSE);
169  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
170 
171  // Fill pipe.
172  nsCAutoString writeData = NS_ConvertUTF16toUTF8(mStdinString);
173  PRInt32 bytesWritten;
174  bytesWritten = PR_Write(stdinWriteFD,
175  writeData.get(),
176  writeData.Length());
177  NS_ENSURE_TRUE(bytesWritten == writeData.Length(), NS_ERROR_FAILURE);
178 
179  // Redirect process stdin.
180  PR_ProcessAttrSetStdioRedirect(processAttr,
181  PR_StandardInput,
182  stdinReadFD);
183 
184  // Keep stdin read descriptor open for the process to read. Close the
185  // stdin write descriptor so that the process gets EOF when all of the
186  // data is read.
187  mStdinReadFD = autoStdinReadFD.forget();
188  PR_Close(autoStdinWriteFD.forget());
189  }
190 
191  // Set up process stdout.
192  if (mPipeStdoutString) {
193  // Create a process stdout pipe and set it up for auto-disposal.
194  PRFileDesc* stdoutReadFD;
195  PRFileDesc* stdoutWriteFD;
196  status = PR_CreatePipe(&stdoutReadFD, &stdoutWriteFD);
197  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
198  sbAutoPRFileDesc autoStdoutReadFD(stdoutReadFD);
199  sbAutoPRFileDesc autoStdoutWriteFD(stdoutWriteFD);
200 
201  // Set up stdout pipe file descriptors.
202  status = PR_SetFDInheritable(stdoutReadFD, PR_FALSE);
203  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
204  status = PR_SetFDInheritable(stdoutWriteFD, PR_TRUE);
205  NS_ENSURE_TRUE(status == PR_SUCCESS, NS_ERROR_FAILURE);
206 
207  // Redirect process stdout.
208  PR_ProcessAttrSetStdioRedirect(processAttr,
209  PR_StandardOutput,
210  stdoutWriteFD);
211 
212  // Keep descriptors.
213  mStdoutReadFD = autoStdoutReadFD.forget();
214  mStdoutWriteFD = autoStdoutWriteFD.forget();
215  }
216 
217  // Create and start running the process.
218  mBaseProcess = PR_CreateProcess(argList[0], argList, NULL, processAttr);
219  NS_ENSURE_TRUE(mBaseProcess, NS_ERROR_FAILURE);
220 
221  // Wait for process done on another thread.
222  nsCOMPtr<nsIRunnable> runnable = NS_NEW_RUNNABLE_METHOD(sbProcess,
223  this,
224  WaitForDone);
225  NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
226  rv = NS_NewThread(getter_AddRefs(mWaitForDoneThread), runnable);
227  NS_ENSURE_SUCCESS(rv, rv);
228  }
229 
230  // Clear process auto-kill.
231  autoSelf.forget();
232 
233  return NS_OK;
234 }
235 
236 
241 nsresult
243 {
244  // Do nothing if process already killed.
245  {
246  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
247  nsAutoLock autoProcessLock(mProcessLock);
248  if (mHasBeenKilled)
249  return NS_OK;
250  mHasBeenKilled = PR_TRUE;
251  }
252 
253  // Kill the base process.
254  if (mBaseProcess)
255  PR_KillProcess(mBaseProcess);
256 
257  // Shutdown the wait for done thread.
258  if (mWaitForDoneThread)
259  mWaitForDoneThread->Shutdown();
260 
261  return NS_OK;
262 }
263 
264 
270 {
271  // Kill the process.
272  Kill();
273 
274  // Detach the base process.
275  //XXXeps detaching the process causes crashes.
276 #if 0
277  if (mBaseProcess)
278  PR_DetachProcess(mBaseProcess);
279 #endif
280  mBaseProcess = nsnull;
281 
282  // Close the process pipes.
283  if (mStdinReadFD)
284  PR_Close(mStdinReadFD);
285  if (mStdoutReadFD)
286  PR_Close(mStdoutReadFD);
287  if (mStdoutWriteFD)
288  PR_Close(mStdoutWriteFD);
289 
290  // Dispose of the process lock.
291  if (mProcessLock)
292  nsAutoLock::DestroyLock(mProcessLock);
293  mProcessLock = nsnull;
294 }
295 
296 
297 //
298 // Getters/setters.
299 //
300 
306 nsresult
307 sbProcess::GetArgList(nsTArray<nsString>& aArgList)
308 {
309  // Operate under the process lock.
310  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
311  nsAutoLock autoProcessLock(mProcessLock);
312 
313  // Get the argument list.
314  PRUint32 argCount = mArgList.Length();
315  aArgList.Clear();
316  for (PRUint32 i = 0; i < argCount; i++) {
317  NS_ENSURE_TRUE(aArgList.AppendElement(mArgList[i]), NS_ERROR_OUT_OF_MEMORY);
318  }
319 
320  return NS_OK;
321 }
322 
323 nsresult
324 sbProcess::SetArgList(const nsTArray<nsString>& aArgList)
325 {
326  // Operate under the process lock.
327  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
328  nsAutoLock autoProcessLock(mProcessLock);
329 
330  // Set the argument list.
331  PRUint32 argCount = aArgList.Length();
332  mArgList.Clear();
333  for (PRUint32 i = 0; i < argCount; i++) {
334  NS_ENSURE_TRUE(mArgList.AppendElement(aArgList[i]), NS_ERROR_OUT_OF_MEMORY);
335  }
336 
337  return NS_OK;
338 }
339 
340 
345 nsresult
346 sbProcess::GetStdinString(nsAString& aStdinString)
347 {
348  // Operate under the process lock.
349  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
350  nsAutoLock autoProcessLock(mProcessLock);
351 
352  // Get the stdin string.
353  if (mPipeStdinString)
354  aStdinString.Assign(mStdinString);
355  else
356  aStdinString.SetIsVoid(PR_TRUE);
357 
358  return NS_OK;
359 }
360 
361 nsresult
362 sbProcess::SetStdinString(const nsAString& aStdinString)
363 {
364  // Operate under the process lock.
365  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
366  nsAutoLock autoProcessLock(mProcessLock);
367 
368  // Set the stdin string.
369  mStdinString.Assign(aStdinString);
370  mPipeStdinString = !aStdinString.IsVoid();
371 
372  return NS_OK;
373 }
374 
375 
380 nsresult
381 sbProcess::GetStdoutString(nsAString& aStdoutString)
382 {
383  // Operate under the process lock.
384  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
385  nsAutoLock autoProcessLock(mProcessLock);
386 
387  // Get the stdout string.
388  if (mPipeStdoutString)
389  aStdoutString.Assign(mStdoutString);
390  else
391  aStdoutString.SetIsVoid(PR_TRUE);
392 
393  return NS_OK;
394 }
395 
396 
401 nsresult
402 sbProcess::GetPipeStdoutString(PRBool* aPipeStdoutString)
403 {
404  NS_ENSURE_ARG_POINTER(aPipeStdoutString);
405 
406  // Operate under the process lock.
407  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
408  nsAutoLock autoProcessLock(mProcessLock);
409 
410  // Get the pipe stdout string flag.
411  *aPipeStdoutString = mPipeStdoutString;
412 
413  return NS_OK;
414 }
415 
416 nsresult
417 sbProcess::SetPipeStdoutString(PRBool aPipeStdoutString)
418 {
419  // Operate under the process lock.
420  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
421  nsAutoLock autoProcessLock(mProcessLock);
422 
423  // Get the pipe stdout string flag.
424  mPipeStdoutString = aPipeStdoutString;
425 
426  return NS_OK;
427 }
428 
429 
434 nsresult
435 sbProcess::GetDoneMonitor(PRMonitor** aDoneMonitor)
436 {
437  NS_ENSURE_ARG_POINTER(aDoneMonitor);
438 
439  // Operate under the process lock.
440  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
441  nsAutoLock autoProcessLock(mProcessLock);
442 
443  // Get the done monitor.
444  *aDoneMonitor = mDoneMonitor;
445 
446  return NS_OK;
447 }
448 
449 nsresult
450 sbProcess::SetDoneMonitor(PRMonitor* aDoneMonitor)
451 {
452  // Operate under the process lock.
453  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
454  nsAutoLock autoProcessLock(mProcessLock);
455 
456  // Set the done monitor.
457  mDoneMonitor = aDoneMonitor;
458 
459  return NS_OK;
460 }
461 
462 
472 nsresult
473 sbProcess::GetIsDone(PRBool* aIsDone)
474 {
475  NS_ENSURE_ARG_POINTER(aIsDone);
476 
477  // Operate under the process lock.
478  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
479  nsAutoLock autoProcessLock(mProcessLock);
480 
481  // Get the done flag.
482  *aIsDone = mIsDone;
483 
484  return NS_OK;
485 }
486 
487 
492 nsresult
493 sbProcess::GetDoneResult(nsresult* aDoneResult)
494 {
495  NS_ENSURE_ARG_POINTER(aDoneResult);
496 
497  // Operate under the process lock.
498  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
499  nsAutoLock autoProcessLock(mProcessLock);
500 
501  // Get the done result.
502  *aDoneResult = mDoneResult;
503 
504  return NS_OK;
505 }
506 
507 
512 nsresult
513 sbProcess::GetExitCode(PRInt32* aExitCode)
514 {
515  NS_ENSURE_ARG_POINTER(aExitCode);
516 
517  // Operate under the process lock.
518  NS_ENSURE_TRUE(mProcessLock, NS_ERROR_NOT_INITIALIZED);
519  nsAutoLock autoProcessLock(mProcessLock);
520 
521  // Get the exit code.
522  *aExitCode = mExitCode;
523 
524  return NS_OK;
525 }
526 
527 
528 //------------------------------------------------------------------------------
529 //
530 // Private process services.
531 //
532 //------------------------------------------------------------------------------
533 
538 sbProcess::sbProcess() :
539  mProcessLock(nsnull),
540  mPipeStdinString(PR_FALSE),
541  mPipeStdoutString(PR_FALSE),
542  mStdinReadFD(nsnull),
543  mStdoutReadFD(nsnull),
544  mStdoutWriteFD(nsnull),
545  mBaseProcess(nsnull),
546  mDoneResult(NS_OK),
547  mExitCode(0),
548  mHasBeenKilled(PR_FALSE),
549  mDoneMonitor(nsnull),
550  mIsDone(PR_FALSE)
551 {
552 }
553 
554 
561 void
562 sbProcess::WaitForDone()
563 {
564  PRStatus status = PR_SUCCESS;
565  nsresult rv = NS_OK;
566 
567  // Wait for the process to complete and get the exit code.
568  PRInt32 exitCode;
569  status = PR_WaitProcess(mBaseProcess, &exitCode);
570  if (status != PR_SUCCESS)
571  rv = NS_ERROR_FAILURE;
572 
573  // Operate under the process lock.
574  {
575  nsAutoLock autoProcessLock(mProcessLock);
576 
577  // Handle the process done event.
578  if (NS_SUCCEEDED(rv))
579  rv = HandleDone();
580 
581  // Check done result of process.
582  if (NS_SUCCEEDED(rv))
583  mExitCode = exitCode;
584  mDoneResult = rv;
585  }
586 
587  // Change the done condition to true. If a done monitor was provided, change
588  // the condition under the monitor and send notification of the change.
589  if (mDoneMonitor) {
590  nsAutoMonitor monitor(mDoneMonitor);
591  {
592  nsAutoLock autoProcessLock(mProcessLock);
593  mIsDone = PR_TRUE;
594  }
595  monitor.Notify();
596  } else {
597  nsAutoLock autoProcessLock(mProcessLock);
598  mIsDone = PR_TRUE;
599  }
600 }
601 
602 
607 nsresult
608 sbProcess::HandleDone()
609 {
610  // Read the process stdout if set to do so.
611  if (mPipeStdoutString) {
612  // Get the stdout read and write file descriptors.
613  PRFileDesc* stdoutReadFD = mStdoutReadFD;
614  PRFileDesc* stdoutWriteFD = mStdoutWriteFD;
615 
616  // Close the write pipe so that reading the pipe returns EOF when all data
617  // is read. Close outside of process lock to avoid any potential for
618  // deadlock.
619  {
620  nsAutoUnlock autoProcessUnlock(mProcessLock);
621  PR_Close(stdoutWriteFD);
622  }
623  mStdoutWriteFD = nsnull;
624 
625  // Read the stdout data into the stdout string.
626  char readData[512];
627  PRInt32 bytesRead;
628  mStdoutString.Truncate();
629  do {
630  // Read the data. A return value of zero indicates EOF. Read outside of
631  // the process lock to avoid any potential for deadlock.
632  {
633  nsAutoUnlock autoProcessUnlock(mProcessLock);
634  bytesRead = PR_Read(stdoutReadFD, readData, sizeof(readData));
635  }
636  NS_ENSURE_TRUE(bytesRead >= 0, NS_ERROR_FAILURE);
637 
638  // Add the data to the stdout string.
639  if (bytesRead) {
640  nsCAutoString dataString;
641  dataString.Assign(readData, bytesRead);
642  mStdoutString.Append(NS_ConvertUTF8toUTF16(dataString));
643  }
644  } while (bytesRead > 0);
645  }
646 
647  return NS_OK;
648 }
649 
650 
651 //------------------------------------------------------------------------------
652 //------------------------------------------------------------------------------
653 //
654 // Songbird process services.
655 //
656 //------------------------------------------------------------------------------
657 //------------------------------------------------------------------------------
658 
677 nsresult SB_RunProcess(sbProcess** aProcess,
678  nsTArray<nsString>& aArgList,
679  const nsAString* aStdin,
680  PRBool aPipeStdoutString,
681  PRMonitor* aDoneMonitor)
682 {
683  nsresult rv;
684 
685  // Create a Songbird process.
686  nsRefPtr<sbProcess> process;
687  rv = sbProcess::New(getter_AddRefs(process));
688  NS_ENSURE_SUCCESS(rv, rv);
689 
690  // Set up the Songbird process.
691  rv = process->SetArgList(aArgList);
692  NS_ENSURE_SUCCESS(rv, rv);
693  if (aStdin) {
694  rv = process->SetStdinString(*aStdin);
695  NS_ENSURE_SUCCESS(rv, rv);
696  }
697  rv = process->SetPipeStdoutString(aPipeStdoutString);
698  NS_ENSURE_SUCCESS(rv, rv);
699  rv = process->SetDoneMonitor(aDoneMonitor);
700  NS_ENSURE_SUCCESS(rv, rv);
701 
702  // Start running the process.
703  rv = process->Run();
704  NS_ENSURE_SUCCESS(rv, rv);
705 
706  // Return results.
707  process.forget(aProcess);
708 
709  return NS_OK;
710 }
711 
NS_IMPL_THREADSAFE_ISUPPORTS0(sbMetadataCrashTracker)
nsresult GetStdoutString(nsAString &aStdoutString)
Definition: sbProcess.cpp:381
return NS_OK
nsresult GetDoneMonitor(PRMonitor **aDoneMonitor)
Definition: sbProcess.cpp:435
Songbird Process Services Definitions.
virtual ~sbProcess()
Definition: sbProcess.cpp:269
nsresult GetExitCode(PRInt32 *aExitCode)
Definition: sbProcess.cpp:513
nsresult GetDoneResult(nsresult *aDoneResult)
Definition: sbProcess.cpp:493
nsresult Kill()
Definition: sbProcess.cpp:242
nsresult SB_RunProcess(sbProcess **aProcess, nsTArray< nsString > &aArgList, const nsAString *aStdin, PRBool aPipeStdoutString, PRMonitor *aDoneMonitor)
Definition: sbProcess.cpp:677
nsresult GetIsDone(PRBool *aIsDone)
Definition: sbProcess.cpp:473
nsresult SetDoneMonitor(PRMonitor *aDoneMonitor)
Definition: sbProcess.cpp:450
nsresult SetPipeStdoutString(PRBool aPipeStdoutString)
Definition: sbProcess.cpp:417
nsresult GetArgList(nsTArray< nsString > &aArgList)
Definition: sbProcess.cpp:307
nsresult GetPipeStdoutString(PRBool *aPipeStdoutString)
Definition: sbProcess.cpp:402
nsresult SetArgList(const nsTArray< nsString > &aArgList)
Definition: sbProcess.cpp:324
static NS_DECL_ISUPPORTS nsresult New(sbProcess **aProcess)
Definition: sbProcess.cpp:87
nsresult Run()
Definition: sbProcess.cpp:112
_getSelectedPageStyle s i
nsresult GetStdinString(nsAString &aStdinString)
Definition: sbProcess.cpp:346
nsresult SetStdinString(const nsAString &aStdinString)
Definition: sbProcess.cpp:362