Merge branch 'master' of ssh://github.com/mrash/fwknop
[fwknop.git] / perl / legacy / fwknop / fwknop_funcs.c
1 /*
2 ********************************************************************************
3 *
4 *  File: fwknop_funcs.c
5 *
6 *  Purpose: fwknop_funcs.c contains several functions that are needed by
7 *           the fwkmd and fwkwd daemons, so putting these functions in
8 *           a single file make sense.
9 *
10 *  Author: Michael Rash (mbr@cipherdyne.org)
11 *
12 *  Version: 0.1
13 *
14 *  Copyright (C) 2004 Michael Rash (mbr@cipherdyne.org)
15 *
16 *  License (GNU Public License):
17 *
18 *     This program is distributed in the hope that it will be useful,
19 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
20 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 *     GNU General Public License for more details.
22 *
23 *     You should have received a copy of the GNU General Public License
24 *     along with this program; if not, write to the Free Software
25 *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
26 *     USA
27 *
28 ********************************************************************************
29 *
30 *  $Id: fwknop_funcs.c 1375 2009-01-09 06:01:14Z mbr $
31 */
32
33 /* includes */
34 #include "fwknop.h"
35
36 /* shared functions */
37 void slogr(const char *ident, const char *msg) {
38     openlog(ident, LOG_DAEMON, LOG_LOCAL7);
39     syslog(LOG_INFO, "%s", msg);
40     closelog();
41     return;
42 }
43
44 void check_unique_pid(const char *pid_file, const char *prog_name)
45 {
46     FILE *pidfile_ptr;
47     pid_t pid;
48     char pid_line[MAX_PID_SIZE+1];
49
50 #ifdef DEBUG
51     fprintf(stderr,
52         "[+] check_unique_pid(): opening pid file %s\n", pid_file);
53 #endif
54
55     if ((pidfile_ptr = fopen(pid_file, "r")) == NULL) {
56         /* the pid file must not exist (or we can't read it), so
57          * return... write_pid() will create it */
58         return;
59     }
60
61     /* read the first line of the pid_file, which will contain the
62      * process id of any running fwknop process */
63     if (fgets(pid_line, MAX_PID_SIZE+1, pidfile_ptr) == NULL) {
64         return;
65     }
66
67     /* turn the pid_line into an integer */
68     pid = atoi(pid_line);
69
70     /* close the pid_file now that we have read it */
71     fclose(pidfile_ptr);
72
73 #ifdef DEBUG
74     fprintf(stderr,
75         "[+] check_unique_pid(): checking pid: %d with kill 0\n", pid);
76 #endif
77
78     if (kill(pid, 0) == 0) {  /* another prog_name is already running */
79         fprintf(stderr, "[*] %s is already running as pid: %d\n",
80             prog_name, pid);
81         exit(EXIT_FAILURE);
82     } else {
83         return;
84     }
85
86     return; /* for completness */
87 }
88
89 void write_pid(const char *pid_file, const pid_t pid)
90 {
91     FILE *pidfile_ptr;
92
93     if ((pidfile_ptr = fopen(pid_file, "w")) == NULL) {
94         /* could not open the pid file */
95         fprintf(stderr, "[*] Could not open the pid file: %s", pid_file);
96         exit(EXIT_FAILURE);
97     }
98
99     /* write the pid to the pid file */
100     if (fprintf(pidfile_ptr, "%d\n", pid) == 0) {
101         fprintf(stderr, "[*] pid: %d could not be written to pid file: %s",
102                 pid, pid_file);
103         exit(EXIT_FAILURE);
104     }
105
106     /* close the pid_file now that we have read it */
107     fclose(pidfile_ptr);
108
109     /* set permissions to 0600 (suggested by Franck Joncourt) */
110     chmod(pid_file, 0600);
111
112     return;
113 }
114
115 int find_char_var(char *search_str, char *charvar, char *line)
116 {
117     char *index_tmp;
118     char *ptEnd;
119     int i;
120
121     /* There is no need to check for this  variable since this one
122      * is already setup */
123     if (*charvar != 0)
124         return 0;
125
126     index_tmp = line;
127
128     /* look for specific variables in the config
129      * file that match the search_str */
130     for (i=0; i < strlen(search_str); i++)
131         if (*index_tmp++ != search_str[i])
132             return 0;
133
134     /* require trailing space char or tab */
135     if (!isspace(*index_tmp++))
136         return 0;
137
138 #ifdef DEBUG
139     fprintf(stderr, "[+] find_char_var(): found %s in line: %s",
140             search_str, line);
141 #endif
142
143     /* go to the beginning of the value */
144     while(isspace(*index_tmp))
145         index_tmp++;
146
147     /* make sure that the variable has a semicolon at the end of the line */
148     ptEnd = strchr(index_tmp, ';');
149     if (ptEnd == NULL) {
150         fprintf(stderr,
151             "[*] find_char_var(): No ending semicolon found for: %s.\n",
152             search_str);
153         exit(EXIT_FAILURE);
154     }
155
156     /* make sure the value is not too long */
157     *ptEnd = '\0';
158     if (strlen(index_tmp) > MAX_GEN_LEN-1) {
159         fprintf(stderr,
160                 "[*] find_char_var(): the config line for %s is too long. ",
161                 search_str);
162         fprintf(stderr, "Exiting.\n");
163         exit(EXIT_FAILURE);
164     }
165
166     /* copy the value to charvar */
167     strlcpy(charvar, index_tmp, MAX_GEN_LEN);
168     return 1;
169 }
170
171 void *safe_malloc(const unsigned int len)
172 {
173     void *buf = NULL;
174     buf = malloc(len);
175     if (buf == NULL) {
176         fprintf(stderr, "[*] Could not malloc() %d bytes\n", len);
177         exit(EXIT_FAILURE);
178     }
179     return buf;
180 }
181
182 int has_sub_var(char *var_name, char *value, char *sub_var,
183     char *pre_str, char *post_str)
184 {
185     unsigned int i, sub_var_ctr = 0, found_sub_var = 0;
186     unsigned int pre_str_ctr = 0, found_pre_str = 1;
187     unsigned int post_str_ctr = 0, found_post_str = 0;
188     unsigned int found_one_var = 0;
189
190     sub_var[0]  = '\0';
191     pre_str[0]  = '\0';
192     post_str[0] = '\0';
193
194     for (i=0; i < strlen(value); i++) {
195         if (found_sub_var) {
196             if (! isdigit(value[i])
197                     && ! isalpha(value[i]) && value[i] != '_') {
198                 found_sub_var  = 0;
199                 found_post_str = 1;
200                 found_one_var  = 1;
201             } else {
202                 if (! found_one_var) {
203                     sub_var[sub_var_ctr] = value[i];
204                     sub_var_ctr++;
205                 }
206             }
207         }
208
209         if (found_pre_str && value[i] != '$') {
210             pre_str[pre_str_ctr] = value[i];
211             pre_str_ctr++;
212         }
213
214         if (found_post_str) {
215             post_str[post_str_ctr] = value[i];
216             post_str_ctr++;
217         }
218
219         if (value[i] == '$') {
220             found_sub_var = 1;
221             found_pre_str = 0;
222             pre_str[pre_str_ctr] = '\0';
223         }
224     }
225     if (sub_var_ctr >= MAX_GEN_LEN - 1) {
226         printf("[*] Sub-var length exceeds maximum of %d chars within var: %s\n",
227                 MAX_GEN_LEN, var_name);
228         exit(EXIT_FAILURE);
229     }
230     if (sub_var[0] != '\0') {
231         sub_var[sub_var_ctr]   = '\0';
232         post_str[post_str_ctr] = '\0';
233         if (strncmp(sub_var, var_name, MAX_GEN_LEN) == 0) {
234             printf("[*] Cannot have identical var to sub-var: %s\n",
235                     sub_var);
236             exit(EXIT_FAILURE);
237         }
238         return 1;
239     }
240
241     return 0;
242 }
243
244 void expand_sub_var_value(char *value, const char *sub_var,
245     const char *pre_str, const char *post_str)
246 {
247     if (strlen(sub_var) + strlen(pre_str) + strlen(post_str)
248             > MAX_GEN_LEN) {
249         printf("[*] Variable value too long: %s%s%s\n",
250             sub_var, pre_str, post_str);
251         exit(EXIT_FAILURE);
252     }
253     strlcpy(value, pre_str, MAX_GEN_LEN);
254     strlcat(value, sub_var, MAX_GEN_LEN);
255     strlcat(value, post_str, MAX_GEN_LEN);
256     return;
257 }
258
259 /*
260  * Do everything required to cleanly become a daemon: fork(), start
261  * a new session, chdir "/", and close un-needed standard filehandles.
262  */
263 void daemonize_process(const char *pid_file)
264 {
265     pid_t child_pid, sid;
266
267     if ((child_pid = fork()) < 0) {
268         fprintf(stderr, "[*] Could not fork()");
269         exit(EXIT_FAILURE);
270     }
271
272     if (child_pid > 0) {
273 #ifdef DEBUG
274         fprintf(stderr, "[+] writing pid: %d to pid file: %s\n",
275                 child_pid, pid_file);
276 #endif
277         write_pid(pid_file, child_pid);
278         exit(EXIT_SUCCESS);   /* exit the parent process */
279     }
280
281     /*
282      * Now we are in the child process
283      */
284
285     /* start a new session */
286     if ((sid = setsid()) < 0) {
287         fprintf(stderr, "[*] setsid() Could not start a new session");
288         exit(EXIT_FAILURE);
289     }
290
291     /* make "/" the current directory */
292     if ((chdir("/")) < 0) {
293         fprintf(stderr, "[*] Could not chdir() to /");
294         exit(EXIT_FAILURE);
295     }
296
297     /* reset the our umask (for completeness) */
298     umask(0);
299
300     /* close un-needed file handles */
301     close(STDIN_FILENO);
302     close(STDOUT_FILENO);
303     close(STDERR_FILENO);
304
305     return;
306 }
307
308 /*
309  * This function sends an email alert.
310  * The mail command is executed from the shell interpreter without relying on
311  * any local environment variable.
312  *
313  * @shCmd: command to use to invoke the shell niterpreter
314  * @fwk_email: structure containing data required to send an email
315  */
316 void send_alert_email(const char *shCmd, fwatch_email fwk_email)
317 {
318     char *cmd_name;
319     char  exec_line[MAX_MSG_LEN];
320     pid_t child_pid;
321
322     /* Set a pointer on the name of the mail command to use */
323     cmd_name = strrchr(fwk_email.cmd, '/');
324     cmd_name = (cmd_name == NULL) ? fwk_email.cmd : cmd_name+1;
325
326     /* Write the invocation of the sendmail command*/
327     if (strncmp(cmd_name, "sendmail", 8) == 0) {
328         snprintf(exec_line, sizeof(exec_line),
329                     "%s -t << EOF\nFrom: %s\nTo: %s\nSubject: %s\n\nEOF",
330                     fwk_email.cmd, fwk_email.sender, fwk_email.recipient,
331                     fwk_email.subject);
332
333     /* Write the invokation of the mail command */
334     } else {
335         snprintf(exec_line, sizeof(exec_line),
336                      "%s -s \"%s\" %s < /dev/null > /dev/null 2>&1",
337                      fwk_email.cmd, fwk_email.subject, fwk_email.recipient);
338     }
339
340     /* Make sure the string is NULL-terminated if truncated */
341     exec_line[sizeof(exec_line)-1] = '\0';
342
343 #ifdef DEBUG
344     fprintf(stderr, "[+] sending mail with the following command:\n%s\n",
345                 exec_line);
346 #endif
347
348     /* Fork and execute the mail command from the sh interpreter
349      * without local env */
350     if ((child_pid = fork()) < 0)
351         exit(EXIT_FAILURE);
352
353     else if (child_pid > 0)
354         wait(NULL);  /* mail better work */
355
356     else {
357         exit(execle(shCmd, shCmd, "-c", exec_line, NULL, NULL));
358     }
359     return;
360 }
361
362 void list_to_array(char *list_ptr, const char sep, char **array,
363         unsigned char max_arg)
364 {
365     unsigned char  ix;
366
367     ix = 0;
368
369     while ( (list_ptr != NULL) && (ix < max_arg) ) {
370
371         array[ix] = list_ptr;
372
373         list_ptr = strchr(list_ptr, sep);
374         if (list_ptr != NULL)
375             *list_ptr++ = '\0';
376
377         ix++;
378     }
379
380     array[ix] = NULL;
381 }