Run latex
[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.13  1995/12/01 12:41:03  adam
51  * Bug fix.
52  *
53  * Revision 1.12  1995/12/01  12:24:17  adam
54  * New function: gw_log_mask_str.
55  *
56  * Revision 1.11  1995/11/09  09:54:28  adam
57  * More readable logging format.
58  *
59  * Revision 1.10  1995/05/16  09:40:48  adam
60  * LICENSE.
61  *
62  * Revision 1.9  1995/04/19  12:12:06  adam
63  * Resource system uses only one log debug level.
64  *
65  * Revision 1.8  1995/04/17  09:36:16  adam
66  * Minor changes.
67  *
68  * Revision 1.7  1995/04/10  13:20:25  adam
69  * Use gettimeofday(2) instead of time(2) to get log time in milliseconds.
70  *
71  * Revision 1.6  1995/03/28  08:01:51  adam
72  * Bug fix: GW_LOG_ERRNO.
73  *
74  * Revision 1.5  1995/03/27  12:51:10  adam
75  * New log level in use: GW_LOG_ERRNO.
76  *
77  * Revision 1.4  1995/02/23  08:32:22  adam
78  * Changed header.
79  *
80  * Revision 1.2  1995/02/17  17:06:56  adam
81  * Remove everything before '/' in app_name. Use compact date format.
82  *
83  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
84  * Initial version of email gateway under CVS control.
85  *
86  * Initial:       Dec  7, 94 (Adam Dickmeiss)
87  */
88
89 #include <stdio.h>
90 #include <stdlib.h>
91 #include <string.h>
92 #include <stdarg.h>
93 #include <fcntl.h>
94 #include <sys/time.h>
95 #include <unistd.h>
96 #include <time.h>
97 #include <errno.h>
98 #include <ctype.h>
99
100 #include <gw-log.h>
101
102 static char *app_name = NULL;
103 static unsigned level = GW_LOG_DEFAULT;
104 static int session = 0;
105
106 struct file_mask {
107     unsigned mask;           /* level mask for this file entry */
108     int fd;                  /* file descriptor for this file */
109     char *fname;             /* name of file ("" if stdout) */
110     struct file_mask *next;  /* next file in chain */
111 };
112
113 struct file_mask *file_mask_list = NULL;
114
115 char *gw_strdup (const char *s)
116 {
117     char *n = malloc (strlen(s)+1);
118     if (n)
119         strcpy (n, s);
120     return n;
121 }
122
123 void gw_log_init (const char *app_name_a)
124 {
125     struct file_mask *list, *list1;
126     const char *cp;
127
128     if ((cp = strrchr (app_name_a, '/')))
129         app_name = gw_strdup (cp+1);
130     else
131         app_name = gw_strdup (app_name_a);
132     level = GW_LOG_DEFAULT;
133     session = 0;
134
135     /* clean up all output file masks... */
136     for (list = file_mask_list; list; list = list1)
137     {
138         if (list->fd > 2)                  /* avoid closing stdout/stderr */
139             close (list->fd);              
140         free (list->fname);
141         list1 = list->next;
142         free (list);
143     }
144     file_mask_list = NULL;
145 }
146
147 void gw_log_level (unsigned level_a)
148 {
149     level = level_a;
150 }
151
152 void gw_log_session (int session_a)
153 {
154     session = session_a;
155 }
156
157 int gw_log_file (unsigned level_a, const char *fname_a)
158 {
159     struct file_mask **listp, *new_file_mask;
160     listp = &file_mask_list;
161
162     /* go through file mask list and close files already associated */
163     /* on new level */
164     while (*listp)
165     {
166         if (!((*listp)->mask &= ~level_a)) /* any levels left on this one? */
167         {
168             if ((*listp)->fd > 2)          /* close if not stdout/stderr */
169                 close ((*listp)->fd);      
170             free ((*listp)->fname);
171             *listp = (*listp)->next;
172         }
173         listp = &(*listp)->next;
174     }
175     if (!fname_a)                          /* stderr? */
176         return 0;                          /* stderr doesn't appear on list */
177     new_file_mask = malloc (sizeof(*new_file_mask));
178     if (!new_file_mask)
179         return -1;
180     new_file_mask->mask = level_a;
181     new_file_mask->next = file_mask_list;
182     file_mask_list = new_file_mask;
183     if (!(file_mask_list->fname = gw_strdup (fname_a)))
184         return -1;
185     if (*fname_a == '\0')                  /* stdout? */
186         new_file_mask->fd = 1;            
187     else                                   /* no, open as usual */
188     {
189         new_file_mask->fd = open (fname_a, O_WRONLY|O_CREAT|O_APPEND, 0666);
190         if (new_file_mask->fd == -1)
191             return -1;
192     }
193     return 0;
194 }
195
196 int gw_log (unsigned level_a, const char *event_type, const char *format, ...)
197 {
198     static char emit_str[4096];
199     struct file_mask *list;
200     struct timeval tv;
201     struct timezone tz;
202     va_list ap;
203     unsigned e_level = level_a & level;
204     int count;
205     int err = 0;
206     struct tm tms;
207     char *cp;
208
209     if (!e_level)                          /* any effective level(s)? */      
210         return 0;
211
212     va_start (ap, format);
213     gettimeofday (&tv, &tz);
214
215     memcpy (&tms, localtime (&tv.tv_sec), sizeof(tms));
216     sprintf (emit_str, "%s %d %02d%02d%02d %02d%02d%02d %03d %d %s ",
217              app_name, session,
218              tms.tm_year, 1+tms.tm_mon, tms.tm_mday,
219              tms.tm_hour, tms.tm_min, tms.tm_sec,
220              (int) (tv.tv_usec/1000),
221              e_level, event_type);
222     if ((cp = strchr (emit_str, '\n')))    /* remove \n from ctime-str */
223         *cp = ' ';
224     count = strlen (emit_str);
225     vsprintf (emit_str+count, format, ap);
226     if (level_a & GW_LOG_ERRNO)
227     {
228         strcat (emit_str, ": ");
229         strcat (emit_str, strerror (errno));
230     }
231     strcat (emit_str, "\n");
232     count = strlen (emit_str);
233
234     /* go through file mask list... */
235     for (list = file_mask_list; list; list = list->next)
236         if (list->mask & e_level)          /* output this one? */
237         {
238             e_level &= ~list->mask;        /* remove from effective level */
239             if (write (list->fd, emit_str, count) != count)
240                 err = 1;
241         }
242     if (e_level)                           /* bits left on effective level? */
243         write (2, emit_str, count);
244     va_end (ap);
245     return err;
246 }
247
248 static struct {
249     int mask;
250     char *name;
251 } mask_names[] =
252 {
253     { GW_LOG_ALL,       "all"    },
254     { GW_LOG_DEFAULT,   "default"},
255     { GW_LOG_DEFAULT,   "def"    },
256     { GW_LOG_FATAL,     "fatal"  },
257     { GW_LOG_WARN,      "warn"   },
258     { GW_LOG_ACCT,      "acct"   },
259     { GW_LOG_STAT,      "stat"   },
260     { GW_LOG_NOTICE,    "notice"   },
261     { GW_LOG_DEBUG,     "debug"  },
262     { GW_LOG_DEBUGN(0), "debug0" },
263     { GW_LOG_DEBUGN(1), "debug1" },
264     { GW_LOG_DEBUGN(2), "debug2" },
265     { GW_LOG_DEBUGN(3), "debug3" },
266     { GW_LOG_DEBUGN(4), "debug4" },
267     { GW_LOG_DEBUGN(5), "debug5" },
268     { GW_LOG_DEBUGN(6), "debug6" },
269     { GW_LOG_DEBUGN(7), "debug7" },
270     { GW_LOG_DEBUGN(8), "debug8" },
271     { GW_LOG_DEBUGN(8), "debug9" },
272     { 0,                "none"   },
273     { 0, NULL }
274 };  
275
276 unsigned gw_log_mask_str (const char *str)
277 {
278     const char *p;
279     int i;
280     unsigned level = GW_LOG_DEFAULT;
281
282     while (*str)
283     {
284         for (p = str; *p && *p != ','; p++)
285             ;
286         if (*str == '-' || isdigit(*str))
287             level = atoi (str);
288         else
289             for (i = 0; mask_names[i].name; i++)
290                 if (strlen (mask_names[i].name) == p-str &&
291                     memcmp (mask_names[i].name, str, p-str) == 0)
292                 {
293                     if (mask_names[i].mask)
294                         level |= mask_names[i].mask;
295                     else
296                         level = 0;
297                 }
298         if (*p == ',')
299             p++;
300         str = p;
301     }
302     return level;
303 }