New function: gw_log_mask_str.
[egate.git] / res+log / gw-log.c
1 /*
2  * Copyright (c) 1995, the EUROPAGATE consortium (see below).
3  *
4  * The EUROPAGATE consortium members are:
5  *
6  *    University College Dublin
7  *    Danmarks Teknologiske Videnscenter
8  *    An Chomhairle Leabharlanna
9  *    Consejo Superior de Investigaciones Cientificas
10  *
11  * Permission to use, copy, modify, distribute, and sell this software and
12  * its documentation, in whole or in part, for any purpose, is hereby granted,
13  * provided that:
14  *
15  * 1. This copyright and permission notice appear in all copies of the
16  * software and its documentation. Notices of copyright or attribution
17  * which appear at the beginning of any file must remain unchanged.
18  *
19  * 2. The names of EUROPAGATE or the project partners may not be used to
20  * endorse or promote products derived from this software without specific
21  * prior written permission.
22  *
23  * 3. Users of this software (implementors and gateway operators) agree to
24  * inform the EUROPAGATE consortium of their use of the software. This
25  * information will be used to evaluate the EUROPAGATE project and the
26  * software, and to plan further developments. The consortium may use
27  * the information in later publications.
28  * 
29  * 4. Users of this software agree to make their best efforts, when
30  * documenting their use of the software, to acknowledge the EUROPAGATE
31  * consortium, and the role played by the software in their work.
32  *
33  * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,
34  * EXPRESS, IMPLIED, OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
35  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
36  * IN NO EVENT SHALL THE EUROPAGATE CONSORTIUM OR ITS MEMBERS BE LIABLE
37  * FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF
38  * ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
39  * OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND
40  * ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE
41  * USE OR PERFORMANCE OF THIS SOFTWARE.
42  *
43  */
44 /*
45  * Implementation of logging facilities.
46  *
47  * Europagate, 1994-1995.
48  *
49  * $Log: gw-log.c,v $
50  * Revision 1.12  1995/12/01 12:24:17  adam
51  * New function: gw_log_mask_str.
52  *
53  * Revision 1.11  1995/11/09  09:54:28  adam
54  * More readable logging format.
55  *
56  * Revision 1.10  1995/05/16  09:40:48  adam
57  * LICENSE.
58  *
59  * Revision 1.9  1995/04/19  12:12:06  adam
60  * Resource system uses only one log debug level.
61  *
62  * Revision 1.8  1995/04/17  09:36:16  adam
63  * Minor changes.
64  *
65  * Revision 1.7  1995/04/10  13:20:25  adam
66  * Use gettimeofday(2) instead of time(2) to get log time in milliseconds.
67  *
68  * Revision 1.6  1995/03/28  08:01:51  adam
69  * Bug fix: GW_LOG_ERRNO.
70  *
71  * Revision 1.5  1995/03/27  12:51:10  adam
72  * New log level in use: GW_LOG_ERRNO.
73  *
74  * Revision 1.4  1995/02/23  08:32:22  adam
75  * Changed header.
76  *
77  * Revision 1.2  1995/02/17  17:06:56  adam
78  * Remove everything before '/' in app_name. Use compact date format.
79  *
80  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
81  * Initial version of email gateway under CVS control.
82  *
83  * Initial:       Dec  7, 94 (Adam Dickmeiss)
84  */
85
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <stdarg.h>
90 #include <fcntl.h>
91 #include <sys/time.h>
92 #include <unistd.h>
93 #include <time.h>
94 #include <errno.h>
95 #include <ctype.h>
96
97 #include <gw-log.h>
98
99 static char *app_name = NULL;
100 static unsigned level = GW_LOG_DEFAULT;
101 static int session = 0;
102
103 struct file_mask {
104     unsigned mask;           /* level mask for this file entry */
105     int fd;                  /* file descriptor for this file */
106     char *fname;             /* name of file ("" if stdout) */
107     struct file_mask *next;  /* next file in chain */
108 };
109
110 struct file_mask *file_mask_list = NULL;
111
112 char *gw_strdup (const char *s)
113 {
114     char *n = malloc (strlen(s)+1);
115     if (n)
116         strcpy (n, s);
117     return n;
118 }
119
120 void gw_log_init (const char *app_name_a)
121 {
122     struct file_mask *list, *list1;
123     const char *cp;
124
125     if ((cp = strrchr (app_name_a, '/')))
126         app_name = gw_strdup (cp+1);
127     else
128         app_name = gw_strdup (app_name_a);
129     level = GW_LOG_DEFAULT;
130     session = 0;
131
132     /* clean up all output file masks... */
133     for (list = file_mask_list; list; list = list1)
134     {
135         if (list->fd > 2)                  /* avoid closing stdout/stderr */
136             close (list->fd);              
137         free (list->fname);
138         list1 = list->next;
139         free (list);
140     }
141     file_mask_list = NULL;
142 }
143
144 void gw_log_level (unsigned level_a)
145 {
146     level = level_a;
147 }
148
149 void gw_log_session (int session_a)
150 {
151     session = session_a;
152 }
153
154 int gw_log_file (unsigned level_a, const char *fname_a)
155 {
156     struct file_mask **listp, *new_file_mask;
157     listp = &file_mask_list;
158
159     /* go through file mask list and close files already associated */
160     /* on new level */
161     while (*listp)
162     {
163         if (!((*listp)->mask &= ~level_a)) /* any levels left on this one? */
164         {
165             if ((*listp)->fd > 2)          /* close if not stdout/stderr */
166                 close ((*listp)->fd);      
167             free ((*listp)->fname);
168             *listp = (*listp)->next;
169         }
170         listp = &(*listp)->next;
171     }
172     if (!fname_a)                          /* stderr? */
173         return 0;                          /* stderr doesn't appear on list */
174     new_file_mask = malloc (sizeof(*new_file_mask));
175     if (!new_file_mask)
176         return -1;
177     new_file_mask->mask = level_a;
178     new_file_mask->next = file_mask_list;
179     file_mask_list = new_file_mask;
180     if (!(file_mask_list->fname = gw_strdup (fname_a)))
181         return -1;
182     if (*fname_a == '\0')                  /* stdout? */
183         new_file_mask->fd = 1;            
184     else                                   /* no, open as usual */
185     {
186         new_file_mask->fd = open (fname_a, O_WRONLY|O_CREAT|O_APPEND, 0666);
187         if (new_file_mask->fd == -1)
188             return -1;
189     }
190     return 0;
191 }
192
193 int gw_log (unsigned level_a, const char *event_type, const char *format, ...)
194 {
195     static char emit_str[4096];
196     struct file_mask *list;
197     struct timeval tv;
198     struct timezone tz;
199     va_list ap;
200     unsigned e_level = level_a & level;
201     int count;
202     int err = 0;
203     struct tm tms;
204     char *cp;
205
206     if (!e_level)                          /* any effective level(s)? */      
207         return 0;
208
209     va_start (ap, format);
210     gettimeofday (&tv, &tz);
211
212     memcpy (&tms, localtime (&tv.tv_sec), sizeof(tms));
213     sprintf (emit_str, "%s %d %02d%02d%02d %02d%02d%02d %03d %d %s ",
214              app_name, session,
215              tms.tm_year, 1+tms.tm_mon, tms.tm_mday,
216              tms.tm_hour, tms.tm_min, tms.tm_sec,
217              (int) (tv.tv_usec/1000),
218              e_level, event_type);
219     if ((cp = strchr (emit_str, '\n')))    /* remove \n from ctime-str */
220         *cp = ' ';
221     count = strlen (emit_str);
222     vsprintf (emit_str+count, format, ap);
223     if (level_a & GW_LOG_ERRNO)
224     {
225         strcat (emit_str, ": ");
226         strcat (emit_str, strerror (errno));
227     }
228     strcat (emit_str, "\n");
229     count = strlen (emit_str);
230
231     /* go through file mask list... */
232     for (list = file_mask_list; list; list = list->next)
233         if (list->mask & e_level)          /* output this one? */
234         {
235             e_level &= ~list->mask;        /* remove from effective level */
236             if (write (list->fd, emit_str, count) != count)
237                 err = 1;
238         }
239     if (e_level)                           /* bits left on effective level? */
240         write (2, emit_str, count);
241     va_end (ap);
242     return err;
243 }
244
245 static struct {
246     int mask;
247     char *name;
248 } mask_names[] =
249 {
250     { GW_LOG_ALL,       "all"    },
251     { GW_LOG_DEFAULT,   "default"},
252     { GW_LOG_DEFAULT,   "def"    },
253     { GW_LOG_FATAL,     "fatal"  },
254     { GW_LOG_WARN,      "warn"   },
255     { GW_LOG_ACCT,      "acct"   },
256     { GW_LOG_STAT,      "stat"   },
257     { GW_LOG_DEBUG,     "debug"  },
258     { GW_LOG_DEBUGN(0), "debug0" },
259     { GW_LOG_DEBUGN(1), "debug1" },
260     { GW_LOG_DEBUGN(2), "debug2" },
261     { GW_LOG_DEBUGN(3), "debug3" },
262     { GW_LOG_DEBUGN(4), "debug4" },
263     { GW_LOG_DEBUGN(5), "debug5" },
264     { GW_LOG_DEBUGN(6), "debug6" },
265     { GW_LOG_DEBUGN(7), "debug7" },
266     { GW_LOG_DEBUGN(8), "debug8" },
267     { GW_LOG_DEBUGN(8), "debug9" },
268     { 0,                "none"   },
269     { 0, NULL }
270 };  
271
272 unsigned gw_log_mask_str (const char *str)
273 {
274     const char *p;
275     int i;
276     unsigned level = GW_LOG_DEFAULT;
277
278     while (*str)
279     {
280         for (p = str; *p && *p != ','; p++)
281             ;
282         if (*str == '-' || isdigit(*str))
283             level = atoi (str);
284         else
285             for (i = 0; mask_names[i].name; i++)
286                 if (strlen (mask_names[i].name) == p-str &&
287                     memcmp (mask_names[i].name, str, p-str) == 0)
288                 {
289                     if (mask_names[i].mask)
290                         level |= mask_names[i].mask;
291                     else
292                         level = 0;
293                 }
294         if (*p == ',')
295             p++;
296         str = p;
297     }
298     return level;
299 }