1 /*
2 * Copyright (C) 2004-2008 Christos Tsantilas
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA.
18 */
19
20 #include <assert.h>
21 #include "common.h"
22 #include "c-icap.h"
23 #include "net_io.h"
24 #include "debug.h"
25 #include "log.h"
26 #include "commands.h"
27 #include "cfg_param.h"
28 #include "registry.h"
29
30 struct schedule_data {
31 char name[CMD_NM_SIZE];
32 time_t when;
33 void *data;
34 };
35
36 /*The list of commands*/
37 static ci_list_t *COMMANDS_LIST = NULL;;
38 /* The list of request for ONDEMAND_CMD commands*/
39 static ci_list_t *COMMANDS_QUEUE = NULL;
40 ci_thread_mutex_t COMMANDS_MTX;
41
commands_init()42 void commands_init()
43 {
44 ci_thread_mutex_init(&COMMANDS_MTX);
45 COMMANDS_LIST = ci_list_create(64, sizeof(ci_command_t));
46 COMMANDS_QUEUE = ci_list_create(64, sizeof(struct schedule_data));
47 }
48
49
register_command(const char * name,int type,void (* command_action)(const char * name,int type,const char ** argv))50 void register_command(const char *name, int type,
51 void (*command_action) (const char *name, int type,
52 const char **argv))
53 {
54 if (! (type & ALL_PROC_CMD)) {
55 ci_debug_printf(1, "Can not register command %s ! Wrong type\n", name );
56 return;
57 }
58 ci_command_t cmd;
59 strncpy(cmd.name, name, CMD_NM_SIZE);
60 cmd.name[CMD_NM_SIZE - 1] = '\0';
61 cmd.type = type;
62 cmd.data = NULL;
63 cmd.command_action = command_action;
64 ci_thread_mutex_lock(&COMMANDS_MTX);
65 ci_list_push(COMMANDS_LIST, &cmd);
66 ci_thread_mutex_unlock(&COMMANDS_MTX);
67 ci_debug_printf(5, "Command %s registered\n", name);
68 }
69
register_command_extend(const char * name,int type,void * data,void (* command_action)(const char * name,int type,void * data))70 void register_command_extend(const char *name, int type, void *data,
71 void (*command_action) (const char *name, int type,
72 void *data))
73 {
74 if (type != CI_CMD_CHILD_START &&
75 type != CI_CMD_CHILD_STOP &&
76 type != CI_CMD_ONDEMAND &&
77 type != CI_CMD_POST_CONFIG &&
78 type != CI_CMD_MONITOR_START &&
79 type != CI_CMD_MONITOR_STOP &&
80 type != CI_CMD_MONITOR_ONDEMAND) {
81 ci_debug_printf(1, "Can not register extend command %s ! wrong type\n", name );
82 return;
83 }
84 ci_command_t cmd;
85 strncpy(cmd.name, name, CMD_NM_SIZE);
86 cmd.name[CMD_NM_SIZE - 1] = '\0';
87 cmd.type = type;
88 cmd.data = data;
89 cmd.command_action_extend = command_action;
90 ci_thread_mutex_lock(&COMMANDS_MTX);
91 ci_list_push(COMMANDS_LIST, &cmd);
92 ci_thread_mutex_unlock(&COMMANDS_MTX);
93 ci_debug_printf(5, "Extend command %s registered\n", name);
94 }
95
commands_reset()96 void commands_reset()
97 {
98 if (COMMANDS_QUEUE) {
99 ci_list_destroy(COMMANDS_QUEUE);
100 COMMANDS_QUEUE = ci_list_create(64, sizeof(struct schedule_data));
101 }
102 if (COMMANDS_LIST) {
103 ci_list_destroy(COMMANDS_LIST);
104 COMMANDS_LIST = ci_list_create(64, sizeof(ci_command_t));
105 }
106 }
107
commands_destroy()108 void commands_destroy()
109 {
110 if (COMMANDS_QUEUE) {
111 ci_list_destroy(COMMANDS_QUEUE);
112 COMMANDS_QUEUE = NULL;
113 }
114 if (COMMANDS_LIST) {
115 ci_list_destroy(COMMANDS_LIST);
116 COMMANDS_LIST = NULL;
117 }
118 }
119
120 /*
121 Currently we are using the following functions which defined in cfg_param.c file
122 These functions must moved to a utils.c file ...
123 */
124 char **split_args(char *args);
125 void free_args(char **argv);
126
cb_check_command(void * data,const void * obj)127 int cb_check_command(void *data, const void *obj)
128 {
129 const ci_command_t **rcommand = (const ci_command_t **)data;
130 const ci_command_t *cur_item = (const ci_command_t *)obj;
131 if (*rcommand && strcmp((*rcommand)->name, cur_item->name) == 0) {
132 *rcommand = cur_item;
133 return 1;
134 }
135
136 return 0;
137 }
138
find_command(const char * cmd_line)139 ci_command_t *find_command(const char *cmd_line)
140 {
141 int len;
142 char *s;
143 ci_command_t tmpCmd;
144 ci_command_t *cmd;
145
146 if (COMMANDS_LIST == NULL) {
147 ci_debug_printf(5, "None command registered\n");
148 return NULL;
149 }
150
151 s = strchr(cmd_line, ' ');
152 if (s)
153 len = s - cmd_line;
154 else
155 len = strlen(cmd_line);
156
157 if (len && len < CMD_NM_SIZE) {
158 strncpy(tmpCmd.name, cmd_line, len);
159 tmpCmd.name[len] = '\0';
160 cmd = &tmpCmd;
161 ci_list_iterate(COMMANDS_LIST, &cmd, cb_check_command);
162 if (cmd != &tmpCmd)
163 /*We found an cmd stored in list. Return it*/
164 return cmd;
165 }
166
167 return NULL;
168 }
169
execute_command(ci_command_t * command,char * cmdline,int exec_type)170 int execute_command(ci_command_t * command, char *cmdline, int exec_type)
171 {
172 char **args;
173
174 if (!command)
175 return 0;
176 args = split_args(cmdline);
177 command->command_action(args[0], exec_type, (const char **)(args + 1));
178 free_args(args);
179 return 1;
180 }
181
exec_cmd_step(void * data,const void * cmd)182 static int exec_cmd_step(void *data, const void *cmd)
183 {
184 int cmd_type = *((int *)data);
185 ci_command_t *command = (ci_command_t *)cmd;
186 ci_debug_printf(7, "Check command: %s, type: %d \n",
187 command->name, command->type);
188 if (command->type == cmd_type) {
189 ci_debug_printf(5, "Execute command:%s \n", command->name);
190 command->command_action_extend (command->name, command->type, command->data);
191 }
192 return 0;
193 }
194
execute_commands_no_lock(int cmd_type)195 int execute_commands_no_lock (int cmd_type)
196 {
197 ci_debug_printf(5, "Going to execute child commands\n");
198 if (COMMANDS_LIST == NULL) {
199 ci_debug_printf(5, "None command registered\n");
200 return 0;
201 }
202 ci_list_iterate(COMMANDS_LIST, &cmd_type, exec_cmd_step);
203 return 1;
204 }
205
commands_execute_start_child()206 int commands_execute_start_child()
207 {
208 return execute_commands_no_lock(CHILD_START_CMD);
209 }
210
commands_execute_stop_child()211 int commands_execute_stop_child()
212 {
213 return execute_commands_no_lock(CHILD_STOP_CMD);
214 }
215
ci_command_register_ctl_cmd(const char * name,int type,void (* command_action)(const char * name,int type,const char ** argv))216 void ci_command_register_ctl_cmd(const char *name, int type, void (*command_action)(const char *name,int type, const char **argv))
217 {
218 register_command(name, type, command_action);
219 }
220
ci_command_register_action(const char * name,int type,void * data,void (* command_action)(const char * name,int type,void * data))221 void ci_command_register_action(const char *name, int type, void *data,
222 void (*command_action) (const char *name, int type, void *data))
223 {
224 register_command_extend(name, type, data, command_action);
225 }
226
ci_command_schedule_on(const char * name,void * data,time_t time)227 void ci_command_schedule_on(const char *name, void *data, time_t time)
228 {
229 struct schedule_data sch;
230 memset(&sch, 0, sizeof(struct schedule_data));
231 strncpy(sch.name, name, CMD_NM_SIZE);
232 sch.name[CMD_NM_SIZE - 1] = '\0';
233 sch.when = time;
234 sch.data = data;
235 if (ci_list_search(COMMANDS_QUEUE, &sch)) {
236 ci_debug_printf(7, "command %s already scheduled for execution on %ld, ignore\n", name, time);
237 return;
238 }
239 ci_thread_mutex_lock(&COMMANDS_MTX);
240 ci_list_push(COMMANDS_QUEUE, &sch);
241 ci_thread_mutex_unlock(&COMMANDS_MTX);
242 ci_debug_printf(9, "command %s scheduled for execution\n", name);
243 }
244
ci_command_schedule(const char * name,void * data,time_t afterSecs)245 void ci_command_schedule(const char *name, void *data, time_t afterSecs)
246 {
247 time_t tm;
248 time(&tm);
249 tm += afterSecs;
250 ci_command_schedule_on(name, data, tm);
251 }
252
253 struct sheduled_cmd_exec_data {
254 time_t tm;
255 int type;
256 };
257
cb_check_queue(void * data,const void * item)258 static int cb_check_queue(void *data, const void *item)
259 {
260 struct schedule_data *sch = (struct schedule_data *)item;
261 struct sheduled_cmd_exec_data *exec_data = (struct sheduled_cmd_exec_data *) data;
262 assert(exec_data);
263 if (sch->when <= exec_data->tm) {
264 ci_command_t *cmd = find_command(sch->name);
265 if (cmd && cmd->type == exec_data->type) {
266 ci_debug_printf(9, "Execute command:%s \n", cmd->name);
267 cmd->command_action_extend (cmd->name, cmd->type, (sch->data ? sch->data : cmd->data));
268 ci_thread_mutex_lock(&COMMANDS_MTX);
269 ci_list_remove(COMMANDS_QUEUE, sch);
270 ci_thread_mutex_unlock(&COMMANDS_MTX);
271 }
272 }
273 return 0;
274 }
275
commands_exec_scheduled(int cmd_type)276 void commands_exec_scheduled(int cmd_type)
277 {
278 struct sheduled_cmd_exec_data exec_data;
279 ci_debug_printf(10, "Going to execute child commands\n");
280 if (COMMANDS_LIST == NULL) {
281 ci_debug_printf(10, "None command registered\n");
282 }
283
284 if (!COMMANDS_QUEUE)
285 return;
286
287 time(&exec_data.tm);
288 exec_data.type = cmd_type;
289 ci_list_iterate(COMMANDS_QUEUE, &exec_data, cb_check_queue);
290 }
291
ci_command_register_child_cleanup(const char * name,void * data,void (* child_cleanup_handler)(const char * name,process_pid_t pid,int reason,void * data))292 void ci_command_register_child_cleanup(const char *name, void *data, void (*child_cleanup_handler) (const char *name, process_pid_t pid, int reason, void *data))
293 {
294 ci_command_t cmd;
295 strncpy(cmd.name, name, CMD_NM_SIZE);
296 cmd.name[CMD_NM_SIZE - 1] = '\0';
297 cmd.type = CI_CMD_CHILD_CLEANUP;
298 cmd.data = data;
299 cmd.command_child_cleanup = child_cleanup_handler;
300 ci_thread_mutex_lock(&COMMANDS_MTX);
301 ci_list_push(COMMANDS_LIST, &cmd);
302 ci_thread_mutex_unlock(&COMMANDS_MTX);
303 ci_debug_printf(5, "Child cleanup command/handler %s registered\n", name);
304 }
305
commands_exec_child_cleanup(process_pid_t pid,int reason)306 void commands_exec_child_cleanup(process_pid_t pid, int reason)
307 {
308 ci_command_t *cmd = NULL;
309 ci_debug_printf(5, "Child cleanup handlers for child %d will be executed\n", pid);
310 ci_thread_mutex_lock(&COMMANDS_MTX);
311 for (cmd = ci_list_first(COMMANDS_LIST); cmd != NULL; cmd = ci_list_next(COMMANDS_LIST)) {
312 if (cmd->type == CI_CMD_CHILD_CLEANUP && cmd->command_child_cleanup)
313 cmd->command_child_cleanup(cmd->name, pid, reason, cmd->data);
314 }
315 ci_thread_mutex_unlock(&COMMANDS_MTX);
316 }
317