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