Resource system uses only one log debug level.
[egate.git] / res+log / gw-log.c
1 /*
2  * Implementation of logging facilities.
3  *
4  * Europagate, 1994-1995.
5  *
6  * $Log: gw-log.c,v $
7  * Revision 1.9  1995/04/19 12:12:06  adam
8  * Resource system uses only one log debug level.
9  *
10  * Revision 1.8  1995/04/17  09:36:16  adam
11  * Minor changes.
12  *
13  * Revision 1.7  1995/04/10  13:20:25  adam
14  * Use gettimeofday(2) instead of time(2) to get log time in milliseconds.
15  *
16  * Revision 1.6  1995/03/28  08:01:51  adam
17  * Bug fix: GW_LOG_ERRNO.
18  *
19  * Revision 1.5  1995/03/27  12:51:10  adam
20  * New log level in use: GW_LOG_ERRNO.
21  *
22  * Revision 1.4  1995/02/23  08:32:22  adam
23  * Changed header.
24  *
25  * Revision 1.2  1995/02/17  17:06:56  adam
26  * Remove everything before '/' in app_name. Use compact date format.
27  *
28  * Revision 1.1.1.1  1995/02/09  17:27:12  adam
29  * Initial version of email gateway under CVS control.
30  *
31  * Initial:       Dec  7, 94 (Adam Dickmeiss)
32  */
33
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <fcntl.h>
39 #include <sys/time.h>
40 #include <unistd.h>
41 #include <time.h>
42 #include <errno.h>
43
44 #include <gw-log.h>
45
46 static char *app_name = NULL;
47 static unsigned level = GW_LOG_DEFAULT;
48 static int session = 0;
49
50 struct file_mask {
51     unsigned mask;           /* level mask for this file entry */
52     int fd;                  /* file descriptor for this file */
53     char *fname;             /* name of file ("" if stdout) */
54     struct file_mask *next;  /* next file in chain */
55 };
56
57 struct file_mask *file_mask_list = NULL;
58
59 char *gw_strdup (const char *s)
60 {
61     char *n = malloc (strlen(s)+1);
62     if (n)
63         strcpy (n, s);
64     return n;
65 }
66
67 void gw_log_init (const char *app_name_a)
68 {
69     struct file_mask *list, *list1;
70     const char *cp;
71
72     if ((cp = strrchr (app_name_a, '/')))
73         app_name = gw_strdup (cp+1);
74     else
75         app_name = gw_strdup (app_name_a);
76     level = GW_LOG_DEFAULT;
77     session = 0;
78
79     /* clean up all output file masks... */
80     for (list = file_mask_list; list; list = list1)
81     {
82         if (list->fd > 2)                  /* avoid closing stdout/stderr */
83             close (list->fd);              
84         free (list->fname);
85         list1 = list->next;
86         free (list);
87     }
88     file_mask_list = NULL;
89 }
90
91 void gw_log_level (unsigned level_a)
92 {
93     level = level_a;
94 }
95
96 void gw_log_session (int session_a)
97 {
98     session = session_a;
99 }
100
101 int gw_log_file (unsigned level_a, const char *fname_a)
102 {
103     struct file_mask **listp, *new_file_mask;
104     listp = &file_mask_list;
105
106     /* go through file mask list and close files already associated */
107     /* on new level */
108     while (*listp)
109     {
110         if (!((*listp)->mask &= ~level_a)) /* any levels left on this one? */
111         {
112             if ((*listp)->fd > 2)          /* close if not stdout/stderr */
113                 close ((*listp)->fd);      
114             free ((*listp)->fname);
115             *listp = (*listp)->next;
116         }
117         listp = &(*listp)->next;
118     }
119     if (!fname_a)                          /* stderr? */
120         return 0;                          /* stderr doesn't appear on list */
121     new_file_mask = malloc (sizeof(*new_file_mask));
122     if (!new_file_mask)
123         return -1;
124     new_file_mask->mask = level_a;
125     new_file_mask->next = file_mask_list;
126     file_mask_list = new_file_mask;
127     if (!(file_mask_list->fname = gw_strdup (fname_a)))
128         return -1;
129     if (*fname_a == '\0')                  /* stdout? */
130         new_file_mask->fd = 1;            
131     else                                   /* no, open as usual */
132     {
133         new_file_mask->fd = open (fname_a, O_WRONLY|O_CREAT|O_APPEND, 0666);
134         if (new_file_mask->fd == -1)
135             return -1;
136     }
137     return 0;
138 }
139
140 int gw_log (unsigned level_a, const char *event_type, const char *format, ...)
141 {
142     static char emit_str[2048];
143     struct file_mask *list;
144     struct timeval tv;
145     struct timezone tz;
146     va_list ap;
147     unsigned e_level = level_a & level;
148     int count;
149     int err = 0;
150     struct tm tms;
151     char *cp;
152
153     if (!e_level)                          /* any effective level(s)? */      
154         return 0;
155
156     va_start (ap, format);
157     gettimeofday (&tv, &tz);
158
159     memcpy (&tms, localtime (&tv.tv_sec), sizeof(tms));
160     sprintf (emit_str, "%s %d %02d%02d%02d%02d%02d%02d%03d %d %s ",
161              app_name, session,
162              tms.tm_year, 1+tms.tm_mon, tms.tm_mday,
163              tms.tm_hour, tms.tm_min, tms.tm_sec,
164              (int) (tv.tv_usec/1000),
165              e_level, event_type);
166     if ((cp = strchr (emit_str, '\n')))    /* remove \n from ctime-str */
167         *cp = ' ';
168     count = strlen (emit_str);
169     vsprintf (emit_str+count, format, ap);
170     if (level_a & GW_LOG_ERRNO)
171     {
172         strcat (emit_str, ": ");
173         strcat (emit_str, strerror (errno));
174     }
175     strcat (emit_str, "\n");
176     count = strlen (emit_str);
177
178     /* go through file mask list... */
179     for (list = file_mask_list; list; list = list->next)
180         if (list->mask & e_level)          /* output this one? */
181         {
182             e_level &= ~list->mask;        /* remove from effective level */
183             if (write (list->fd, emit_str, count) != count)
184                 err = 1;
185         }
186     if (e_level)                           /* bits left on effective level? */
187         write (2, emit_str, count);
188     va_end (ap);
189     return err;
190 }