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