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,
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
)
40 set_thread_concurrency();
41 status
= pthread_create(&tid
, NULL
, (void *(*)(void *))action
, (void *)ups
);
43 log_event(ups
, LOG_WARNING
, "Unable to start thread: ERR=%s\n",
48 t_index
= num_threads
;
49 if (num_threads
< MAX_THREADS
) {
50 thread_id
[num_threads
] = tid
;
51 thread_name
[num_threads
] = proctitle
;
54 log_event(ups
, LOG_ERR
,
55 "Something is wrong, we have %d threads, max is %d\n",
56 num_threads
+ 1, MAX_THREADS
);
62 /* Cancel all running threads except ourselves */
63 void clean_threads(void)
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
]);
79 char sbindir
[MAXSTRING
];
81 int execute_command(UPSINFO
*ups
, UPSCOMMANDS cmd
)
83 char cmdline
[MAXSTRING
];
85 PROCESS_INFORMATION procinfo
;
86 STARTUPINFOA startinfo
;
89 if (cmd
.pid
&& (kill(cmd
.pid
, 0) == 0)) {
91 * Command is already running. No point in running it two
97 /* Find command interpreter */
98 comspec
= getenv("COMSPEC");
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
);
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
132 NULL
, // use parent's environment
133 ups
->scriptdir
, // working directory
134 &startinfo
, // STARTUPINFO pointer
135 &procinfo
); // receives PROCESS_INFORMATION
137 log_event(ups
, LOG_WARNING
, "execute failed: CreateProcessA(NULL, %s, ...)=%d\n",
138 cmdline
, GetLastError());
143 cmd
.pid
= procinfo
.dwProcessId
;
145 /* Don't need handles */
146 CloseHandle(procinfo
.hProcess
);
147 CloseHandle(procinfo
.hThread
);
152 int execute_command(UPSINFO
*ups
, UPSCOMMANDS cmd
)
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
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
);
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 */
178 if (spawnv(P_NOWAIT
, apccontrol
, (char * const *)argv
) == -1) {
179 log_event(ups
, LOG_WARNING
, "execute: cannot spawn(). ERR=%s",
184 /* fork() and exec() */
185 switch (cmd
.pid
= fork()) {
187 log_event(ups
, LOG_WARNING
, "execute: cannot fork(). ERR=%s",
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
) {
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 */
206 execv(apccontrol
, (char **)argv
);
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. */
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
226 if (ups
->num_execed_children
< 0)
227 ups
->num_execed_children
= 1;
229 ups
->num_execed_children
++;
231 while (ups
->num_execed_children
> 0 && waitpid(-1, NULL
, WNOHANG
) > 0)
232 ups
->num_execed_children
--;
236 #endif /* HAVE_QNX_OS */
240 #endif /* HAVE_MINGW */