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