fixing bio testcase and a bug in bio.c, also indenting
[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
200       (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
201        &proc))
202     {
203       SetErrnoFromWinError (GetLastError ());
204       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
205       return -1;
206     }
207   CloseHandle (proc.hProcess);
208   CloseHandle (proc.hThread);
209
210   GNUNET_free (cmd);
211
212   return proc.dwProcessId;
213 #endif
214 }
215
216
217
218
219 /**
220  * Start a process.
221  *
222  * @param filename name of the binary
223  * @param argv NULL-terminated list of arguments to the process
224  * @return process ID of the new process, -1 on error
225  */
226 pid_t
227 GNUNET_OS_start_process_v (const char *filename, char *const argv[])
228 {
229 #ifndef MINGW
230   pid_t ret;
231
232 #if HAVE_WORKING_VFORK
233   ret = vfork ();
234 #else
235   ret = fork ();
236 #endif
237   if (ret != 0)
238     {
239       if (ret == -1)
240         {
241           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
242         }
243       else
244         {
245 #if HAVE_WORKING_VFORK
246           /* let's hope vfork actually works; for some extreme cases (including
247              a testcase) we need 'execvp' to have run before we return, since
248              we may send a signal to the process next and we don't want it
249              to be caught by OUR signal handler (but either by the default
250              handler or the actual handler as installed by the process itself). */
251 #else
252           /* let's give the child process a chance to run execvp, 1s should
253              be plenty in practice */
254           sleep (1);
255 #endif
256         }
257       return ret;
258     }
259   execvp (filename, argv);
260   GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
261   _exit (1);
262 #else
263   char **arg;
264   unsigned int cmdlen;
265   char *cmd, *idx;
266   STARTUPINFO start;
267   PROCESS_INFORMATION proc;
268
269   cmdlen = 0;
270   arg = argv;
271   while (*arg)
272     {
273       cmdlen = cmdlen + strlen (*arg) + 3;
274       arg++;
275     }
276
277   cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
278   arg = argv;
279   while (*arg)
280     {
281       idx += sprintf (idx, "\"%s\" ", *arg);
282       arg++;
283     }
284
285   memset (&start, 0, sizeof (start));
286   start.cb = sizeof (start);
287
288   if (!CreateProcess
289       (filename, cmd, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &start,
290        &proc))
291     {
292       SetErrnoFromWinError (GetLastError ());
293       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "fork");
294       return -1;
295     }
296   CloseHandle (proc.hProcess);
297   CloseHandle (proc.hThread);
298
299   GNUNET_free (cmd);
300
301   return proc.dwProcessId;
302 #endif
303 }
304
305 /**
306  * Retrieve the status of a process
307  * @param proc process ID
308  * @param type status type
309  * @param code return code/signal number
310  * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
311  */
312 int
313 GNUNET_OS_process_status (pid_t proc, enum GNUNET_OS_ProcessStatusType *type,
314                           unsigned long *code)
315 {
316 #ifndef MINGW
317   int status;
318   int ret;
319
320   GNUNET_assert (0 != proc);
321   ret = waitpid (proc, &status, WNOHANG);
322   if (0 == ret)
323     {
324       *type = GNUNET_OS_PROCESS_RUNNING;
325       *code = 0;
326       return GNUNET_NO;
327     }
328   if (proc != ret)
329     {
330       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
331       return GNUNET_SYSERR;
332     }
333   if (WIFEXITED (status))
334     {
335       *type = GNUNET_OS_PROCESS_EXITED;
336       *code = WEXITSTATUS (status);
337     }
338   else if (WIFSIGNALED (status))
339     {
340       *type = GNUNET_OS_PROCESS_SIGNALED;
341       *code = WTERMSIG (status);
342     }
343   else if (WIFSTOPPED (status))
344     {
345       *type = GNUNET_OS_PROCESS_SIGNALED;
346       *code = WSTOPSIG (status);
347     }
348   else if (WIFCONTINUED (status))
349     {
350       *type = GNUNET_OS_PROCESS_RUNNING;
351       *code = 0;
352     }
353   else
354     {
355       *type = GNUNET_OS_PROCESS_UNKNOWN;
356       *code = 0;
357     }
358 #else
359   HANDLE h;
360   DWORD c;
361
362   h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
363   if (INVALID_HANDLE_VALUE == h)
364     {
365       SetErrnoFromWinError (GetLastError ());
366       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "OpenProcess");
367       return GNUNET_SYSERR;
368     }
369
370   c = GetExitCodeProcess (proc, &c);
371   if (STILL_ACTIVE == c)
372     {
373       *type = GNUNET_OS_PROCESS_RUNNING;
374       *code = 0;
375       CloseHandle (h);
376       return GNUNET_NO;
377     }
378   *type = GNUNET_OS_PROCESS_EXITED;
379   *code = c;
380   CloseHandle (h);
381 #endif
382
383   return GNUNET_OK;
384 }
385
386 /**
387  * Wait for a process
388  * @param proc process ID to wait for
389  * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
390  */
391 int
392 GNUNET_OS_process_wait (pid_t proc)
393 {
394 #ifndef MINGW
395   if (proc != waitpid (proc, NULL, 0))
396     return GNUNET_SYSERR;
397
398   return GNUNET_OK;
399 #else
400   HANDLE h;
401   DWORD c;
402   int ret;
403
404   h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, proc);
405   if (INVALID_HANDLE_VALUE == h)
406     {
407       SetErrnoFromWinError (GetLastError ());
408       return GNUNET_SYSERR;
409     }
410
411   if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
412     {
413       SetErrnoFromWinError (GetLastError ());
414       ret = GNUNET_SYSERR;
415     }
416   else
417     ret = GNUNET_OK;
418
419   CloseHandle (h);
420
421   return ret;
422 #endif
423 }
424
425
426 /* end of os_priority.c */