UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / lib / apcexec.c
bloba11777987ae27657d20bef394cfb2f7a094cf365
1 /*
2 * apcexec.c
4 * Fork/exec functions.
5 */
7 /*
8 * Copyright (C) 2000-2004 Kern Sibbald
9 * Copyright (C) 1996-99 Andre M. Hedrick <andre@suse.com>
10 * Copyright (C) 1999-2000 Riccardo Facchetti <riccardo@master.oasi.gpa.it>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of version 2 of the GNU General
14 * Public License as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public
22 * License along with this program; if not, write to the Free
23 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24 * MA 02111-1307, USA.
27 #include "apc.h"
29 static pthread_t thread_id[MAX_THREADS];
30 static const char *thread_name[MAX_THREADS];
31 static int num_threads = 0;
33 /* Start a "real" POSIX thread */
34 int start_thread(UPSINFO *ups, void (*action) (UPSINFO * ups),
35 const char *proctitle, char *argv0)
37 pthread_t tid;
38 int status, t_index;
40 set_thread_concurrency();
41 status = pthread_create(&tid, NULL, (void *(*)(void *))action, (void *)ups);
42 if (status != 0) {
43 log_event(ups, LOG_WARNING, "Unable to start thread: ERR=%s\n",
44 strerror(status));
45 return 0;
48 t_index = num_threads;
49 if (num_threads < MAX_THREADS) {
50 thread_id[num_threads] = tid;
51 thread_name[num_threads] = proctitle;
52 num_threads++;
53 } else {
54 log_event(ups, LOG_ERR,
55 "Something is wrong, we have %d threads, max is %d\n",
56 num_threads + 1, MAX_THREADS);
59 return t_index;
62 /* Cancel all running threads except ourselves */
63 void clean_threads(void)
65 int i;
66 pthread_t my_tid;
68 my_tid = pthread_self();
69 for (i = 0; i < num_threads; i++) {
70 if (!pthread_equal(my_tid, thread_id[i])) {
71 pthread_cancel(thread_id[i]);
72 pthread_detach(thread_id[i]);
77 #ifdef HAVE_MINGW
79 char sbindir[MAXSTRING];
81 int execute_command(UPSINFO *ups, UPSCOMMANDS cmd)
83 char cmdline[MAXSTRING];
84 char *comspec;
85 PROCESS_INFORMATION procinfo;
86 STARTUPINFOA startinfo;
87 BOOL rc;
89 if (cmd.pid && (kill(cmd.pid, 0) == 0)) {
91 * Command is already running. No point in running it two
92 * times.
94 return SUCCESS;
97 /* Find command interpreter */
98 comspec = getenv("COMSPEC");
99 if (comspec == NULL)
100 return FAILURE;
102 /* Build the command line */
103 if (g_os_version_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
104 /* Win95/98/ME need environment size parameter and no extra quotes */
105 asnprintf(cmdline, sizeof(cmdline),
106 "\"%s\" /E:4096 /c \"%s%s\" %s %s %d %d \"%s\"",
107 comspec, ups->scriptdir, APCCONTROL_FILE, cmd.command,
108 ups->upsname, !ups->is_slave(), ups->is_plugged(), sbindir);
109 } else {
110 /* WinNT/2K/Vista need quotes around the entire sub-command */
111 asnprintf(cmdline, sizeof(cmdline),
112 "\"%s\" /c \"\"%s%s\" %s %s %d %d \"%s\"\"",
113 comspec, ups->scriptdir, APCCONTROL_FILE, cmd.command,
114 ups->upsname, !ups->is_slave(), ups->is_plugged(), sbindir);
117 /* Initialize the STARTUPINFOA struct to hide the console window */
118 memset(&startinfo, 0, sizeof(startinfo));
119 startinfo.cb = sizeof(startinfo);
120 startinfo.dwFlags = STARTF_USESHOWWINDOW;
121 startinfo.wShowWindow = SW_HIDE;
123 Dmsg1(200, "execute_command: CreateProcessA(NULL, %s, ...)\n", cmdline);
125 /* Execute the process */
126 rc = CreateProcessA(NULL,
127 cmdline, // command line
128 NULL, // process security attributes
129 NULL, // primary thread security attributes
130 TRUE, // handles are inherited
131 0, // creation flags
132 NULL, // use parent's environment
133 ups->scriptdir, // working directory
134 &startinfo, // STARTUPINFO pointer
135 &procinfo); // receives PROCESS_INFORMATION
136 if (!rc) {
137 log_event(ups, LOG_WARNING, "execute failed: CreateProcessA(NULL, %s, ...)=%d\n",
138 cmdline, GetLastError());
139 return FAILURE;
142 /* Extract pid */
143 cmd.pid = procinfo.dwProcessId;
145 /* Don't need handles */
146 CloseHandle(procinfo.hProcess);
147 CloseHandle(procinfo.hThread);
149 return SUCCESS;
151 #else
152 int execute_command(UPSINFO *ups, UPSCOMMANDS cmd)
154 const char *argv[6];
155 char connected[20], powered[20];
156 char apccontrol[MAXSTRING];
158 if (cmd.pid && (kill(cmd.pid, 0) == 0)) {
160 * Command is already running. No point in running it two
161 * times.
163 return SUCCESS;
166 asnprintf(connected, sizeof(connected), "%d", !ups->is_slave());
167 asnprintf(powered, sizeof(powered), "%d", (int)ups->is_plugged());
168 asnprintf(apccontrol, sizeof(apccontrol), "%s%s", ups->scriptdir, APCCONTROL_FILE);
170 #ifdef HAVE_QNX_OS
171 /* fork() is supported only in single-threaded applications */
172 argv[0] = apccontrol; /* Shell script to execute. */
173 argv[1] = cmd.command; /* Parameter to script. */
174 argv[2] = ups->upsname; /* UPS name */
175 argv[3] = connected;
176 argv[4] = powered;
177 argv[5] = NULL;
178 if (spawnv(P_NOWAIT, apccontrol, (char * const *)argv) == -1) {
179 log_event(ups, LOG_WARNING, "execute: cannot spawn(). ERR=%s",
180 strerror(errno));
181 return FAILURE;
183 #else
184 /* fork() and exec() */
185 switch (cmd.pid = fork()) {
186 case -1: /* error */
187 log_event(ups, LOG_WARNING, "execute: cannot fork(). ERR=%s",
188 strerror(errno));
189 return FAILURE;
191 case 0: /* child */
192 /* Don't leak unnecessary fds to child */
193 for (int i=0; i<sysconf(_SC_OPEN_MAX); i++) {
194 if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO) {
195 if (close(i) == 0)
196 Dmsg1(200, "exec closed fd %d\n", i);
200 argv[0] = apccontrol; /* Shell script to execute. */
201 argv[1] = cmd.command; /* Parameter to script. */
202 argv[2] = ups->upsname; /* UPS name */
203 argv[3] = connected;
204 argv[4] = powered;
205 argv[5] = NULL;
206 execv(apccontrol, (char **)argv);
208 /* NOT REACHED */
209 log_event(ups, LOG_WARNING, "Cannot exec %s %s: %s",
210 apccontrol, cmd.command, strerror(errno));
212 /* Child must exit if fails exec'ing. */
213 exit(-1);
214 break;
216 default: /* parent */
218 * NOTE, we do a nonblocking waitpid) here to
219 * pick up any previous children. We also]
220 * increment a counter, and then in do_action()
221 * in apcaction.c, we wait again each pass until
222 * all the children are reaped. This is for
223 * BSD systems where SIG_IGN does not prevent
224 * zombies.
226 if (ups->num_execed_children < 0)
227 ups->num_execed_children = 1;
228 else
229 ups->num_execed_children++;
231 while (ups->num_execed_children > 0 && waitpid(-1, NULL, WNOHANG) > 0)
232 ups->num_execed_children--;
234 break;
236 #endif /* HAVE_QNX_OS */
238 return SUCCESS;
240 #endif /* HAVE_MINGW */