bio test fixed
[oweals/gnunet.git] / src / util / os_priority.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 2, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/os_priority.c
23  * @brief Methods to set process priority
24  * @author Nils Durner
25  */
26
27 #include "platform.h"
28 #include "gnunet_common.h"
29 #include "gnunet_os_lib.h"
30
31 /**
32  * Set process priority
33  *
34  * @param proc id of the process
35  * @param prio priority value
36  * @return GNUNET_OK on success, GNUNET_SYSERR on error
37  */
38 int
39 GNUNET_OS_set_process_priority (pid_t proc,
40                                 enum GNUNET_SCHEDULER_Priority prio)
41 {
42   int rprio = 0;
43
44   GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
45   if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
46     return GNUNET_OK;
47   /* convert to MINGW/Unix values */
48   switch (prio)
49     {
50     case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
51 #ifdef MINGW
52       rprio = NORMAL_PRIORITY_CLASS;
53 #else
54       rprio = 0;
55 #endif
56       break;
57     case GNUNET_SCHEDULER_PRIORITY_HIGH:
58 #ifdef MINGW
59       rprio = ABOVE_NORMAL_PRIORITY_CLASS;
60 #else
61       rprio = -5;
62 #endif
63       break;
64     case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
65 #ifdef MINGW
66       rprio = BELOW_NORMAL_PRIORITY_CLASS;
67 #else
68       rprio = 10;
69 #endif
70       break;
71     case GNUNET_SCHEDULER_PRIORITY_UI:
72     case GNUNET_SCHEDULER_PRIORITY_URGENT:
73 #ifdef MINGW
74       rprio = HIGH_PRIORITY_CLASS;
75 #else
76       rprio = -10;
77 #endif
78       break;
79     case GNUNET_SCHEDULER_PRIORITY_IDLE:
80 #ifdef MINGW
81       rprio = IDLE_PRIORITY_CLASS;
82 #else
83       rprio = 19;
84 #endif
85       break;
86     default:
87       GNUNET_assert (0);
88       return GNUNET_SYSERR;
89     }
90   /* Set process priority */
91 #ifdef MINGW
92   SetPriorityClass (GetCurrentProcess (), rprio);
93 #else
94   if (proc == getpid ())
95     {
96       errno = 0;
97       if ((-1 == nice (rprio)) && (errno != 0))
98         {
99           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
100                                GNUNET_ERROR_TYPE_BULK, "nice");
101           return GNUNET_SYSERR;
102         }
103     }
104   else
105     {
106       if (0 != setpriority (PRIO_PROCESS, proc, rprio))
107
108         {
109           GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING |
110                                GNUNET_ERROR_TYPE_BULK, "setpriority");
111           return GNUNET_SYSERR;
112         }
113     }
114 #endif
115   return GNUNET_OK;
116 }
117
118
119 /**
120  * Start a process.
121  *
122  * @param filename name of the binary
123  * @param ... NULL-terminated list of arguments to the process
124  * @return process ID of the new process, -1 on error
125  */
126 pid_t
127 GNUNET_OS_start_process (const char *filename, ...)
128 {
129   va_list ap;
130
131 #ifndef MINGW
132   pid_t ret;
133   char **argv;
134   int argc;
135
136 #if HAVE_WORKING_VFORK
137   ret = vfork ();
138 #else
139   ret = fork ();
140 #endif
141   if (ret != 0)
142     {
143       if (ret == -1)
144         {
145           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
146         }
147       else
148         {
149 #if HAVE_WORKING_VFORK
150           /* let's hope vfork actually works; for some extreme cases (including
151              a testcase) we need 'execvp' to have run before we return, since
152              we may send a signal to the process next and we don't want it
153              to be caught by OUR signal handler (but either by the default
154              handler or the actual handler as installed by the process itself). */
155 #else
156           /* let's give the child process a chance to run execvp, 1s should
157              be plenty in practice */
158           sleep (1);
159 #endif
160         }
161       return ret;
162     }
163   argc = 0;
164   va_start (ap, filename);
165   while (NULL != va_arg (ap, char *))
166       argc++;
167   va_end (ap);
168   argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
169   argc = 0;
170   va_start (ap, filename);
171   while (NULL != (argv[argc] = va_arg (ap, char *)))
172       argc++;
173   va_end (ap);
174   execvp (filename, argv);
175   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
176   _exit (1);
177 #else
178   char *arg;
179   unsigned int cmdlen;
180   char *cmd, *idx;
181   STARTUPINFO start;
182   PROCESS_INFORMATION proc;
183
184   cmdlen = 0;
185   va_start (ap, filename);
186   while (NULL != (arg = va_arg (ap, char *)))
187     cmdlen = cmdlen + strlen (arg) + 3;
188   va_end (ap);
189
190   cmd = idx = GNUNET_malloc (sizeof(char) * cmdlen);
191   va_start (ap, filename);
192   while (NULL != (arg = va_arg (ap, char *)))
193     idx += sprintf (idx, "\"%s\" ", arg);
194   va_end (ap);
195
196   memset (&start, 0, sizeof(start));
197   start.cb = sizeof(start);
198
199   if (!CreateProcess (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL,
200       NULL, &start, &proc))
201   {
202     SetErrnoFromWinError (GetLastError ());
203     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
204     return -1;
205   }
206   CloseHandle (proc.hProcess);
207   CloseHandle (proc.hThread);
208
209   GNUNET_free(cmd);
210
211   return proc.dwProcessId;
212 #endif
213 }
214
215
216
217
218 /**
219  * Start a process.
220  *
221  * @param filename name of the binary
222  * @param argv NULL-terminated list of arguments to the process
223  * @return process ID of the new process, -1 on error
224  */
225 pid_t
226 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
227 {
228 #ifndef MINGW
229   pid_t ret;
230
231 #if HAVE_WORKING_VFORK
232   ret = vfork ();
233 #else
234   ret = fork ();
235 #endif
236   if (ret != 0)
237     {
238       if (ret == -1)
239         {
240           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
241         }
242       else
243         {
244 #if HAVE_WORKING_VFORK
245           /* let's hope vfork actually works; for some extreme cases (including
246              a testcase) we need 'execvp' to have run before we return, since
247              we may send a signal to the process next and we don't want it
248              to be caught by OUR signal handler (but either by the default
249              handler or the actual handler as installed by the process itself). */
250 #else
251           /* let's give the child process a chance to run execvp, 1s should
252              be plenty in practice */
253           sleep (1);
254 #endif
255         }
256       return ret;
257     }
258   execvp (filename, argv);
259   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
260   _exit (1);
261 #else
262   char **arg;
263   unsigned int cmdlen;
264   char *cmd, *idx;
265   STARTUPINFO start;
266   PROCESS_INFORMATION proc;
267
268   cmdlen = 0;
269   arg = argv;
270   while (*arg)
271   {
272     cmdlen = cmdlen + strlen (*arg) + 3;
273     arg++;
274   }
275
276   cmd = idx = GNUNET_malloc (sizeof(char) * cmdlen);
277   arg = argv;
278   while (*arg)
279   {
280     idx += sprintf (idx, "\"%s\" ", *arg);
281     arg++;
282   }
283
284   memset (&start, 0, sizeof(start));
285   start.cb = sizeof(start);
286
287   if (!CreateProcess (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL,
288       NULL, &start, &proc))
289   {
290     SetErrnoFromWinError (GetLastError ());
291     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
292     return -1;
293   }
294   CloseHandle (proc.hProcess);
295   CloseHandle (proc.hThread);
296
297   GNUNET_free(cmd);
298
299   return proc.dwProcessId;
300 #endif
301 }
302
303 /**
304  * Retrieve the status of a process
305  * @param proc process ID
306  * @param type status type
307  * @param code return code/signal number
308  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
309  */
310 int
311 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
312     unsigned long *code)
313 {
314 #ifndef MINGW
315   int status;
316   int ret;
317
318   GNUNET_assert (0 != proc);
319   ret = waitpid (proc, &status, WNOHANG);
320   if (0 == ret) 
321     {
322       *type = GNUNET_OS_PROCESS_RUNNING;
323       *code = 0;
324       return GNUNET_NO;
325     }
326   if (proc != ret)
327     {
328       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
329       return GNUNET_SYSERR;
330     }
331   if (WIFEXITED (status))
332   {
333     *type = GNUNET_OS_PROCESS_EXITED;
334     *code = WEXITSTATUS (status);
335   }
336   else if (WIFSIGNALED (status))
337   {
338     *type = GNUNET_OS_PROCESS_SIGNALED;
339     *code = WTERMSIG (status);
340   }
341   else if (WIFSTOPPED (status))
342   {
343     *type = GNUNET_OS_PROCESS_SIGNALED;
344     *code = WSTOPSIG (status);
345   }
346   else if (WIFCONTINUED (status))
347   {
348     *type = GNUNET_OS_PROCESS_RUNNING;
349     *code = 0;
350   }
351   else
352   {
353     *type = GNUNET_OS_PROCESS_UNKNOWN;
354     *code = 0;
355   }
356 #else
357   HANDLE h;
358   DWORD c;
359
360   h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
361   if (INVALID_HANDLE_VALUE == h)
362   {
363     SetErrnoFromWinError (GetLastError ());
364     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
365     return GNUNET_SYSERR;
366   }
367
368   c = GetExitCodeProcess (proc, &c);
369   if (STILL_ACTIVE == c)
370   {
371     *type = GNUNET_OS_PROCESS_RUNNING;
372     *code = 0;
373     CloseHandle (h);
374     return GNUNET_NO;
375   }
376   *type = GNUNET_OS_PROCESS_EXITED;
377   *code = c;
378   CloseHandle (h);
379 #endif
380
381   return GNUNET_OK;
382 }
383
384 /**
385  * Wait for a process
386  * @param proc process ID to wait for
387  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
388  */
389 int
390 GNUNET_OS_process_wait (pid_t proc)
391 {
392 #ifndef MINGW
393   if (proc != waitpid (proc, NULL, 0))
394     return GNUNET_SYSERR;
395
396   return GNUNET_OK;
397 #else
398   HANDLE h;
399   DWORD c;
400   int ret;
401
402   h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
403   if (INVALID_HANDLE_VALUE == h)
404   {
405     SetErrnoFromWinError (GetLastError ());
406     return GNUNET_SYSERR;
407   }
408
409   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
410   {
411     SetErrnoFromWinError (GetLastError ());
412     ret = GNUNET_SYSERR;
413   }
414   else
415     ret = GNUNET_OK;
416
417   CloseHandle (h);
418
419   return ret;
420 #endif
421 }
422
423
424 /* end of os_priority.c */