1 /**
2  * Utilities for the quality of service module mod_qos.
3  *
4  * See http://mod-qos.sourceforge.net/ for further
5  * details.
6  *
7  * Copyright (C) 2020 Pascal Buchbinder
8  *
9  * Licensed to the Apache Software Foundation (ASF) under one or more
10  * contributor license agreements.  See the NOTICE file distributed with
11  * this work for additional information regarding copyright ownership.
12  * The ASF licenses this file to You under the Apache License, Version 2.0
13  * (the "License"); you may not use this file except in compliance with
14  * the License.  You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 
25 static const char revision[] = "$Id: qs_util.c 2595 2020-01-03 06:19:53Z pbuchbinder $";
26 
27 #include <stdio.h>
28 #include <pthread.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <stdarg.h>
33 #include <fcntl.h>
34 #include <dirent.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <pwd.h>
38 
39 #include "qs_util.h"
40 
41 /* mutex for counter access */
42 static pthread_mutex_t m_qs_lock_cs;
43 /* online/offline mode */
44 static int m_qs_offline = 0;
45 /* internal clock for offline analysis
46  * stores time in seconds */
47 static time_t m_qs_virtualSystemTime = 0;
48 
49 /* ----------------------------------
50  * functions
51  * ---------------------------------- */
52 
53 /**
54  * man:
55  * - escape special chars, like "\" and "-"
56  * - wipe leading spaces
57  * - wipe tailing LF
58  */
qs_man_print(int man,const char * fmt,...)59 void qs_man_print(int man, const char *fmt, ...) {
60   char bufin[4096];
61   char bufout[4096];
62   va_list args;
63   int i = 0;
64   int j = 0;
65   memset(bufin, 0, 4096);
66   va_start(args, fmt);
67   vsprintf(bufin, fmt, args);
68   if(man) {
69     // wipe leading spaces
70     //    while(bufin[i] == ' ' && bufin[i+1] == ' ') {
71     while(bufin[i] == ' ') {
72       i++;
73     }
74   }
75   while(bufin[i] && j < 4000) {
76     // escape "\\" and "-" for man page
77     if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
78       bufout[j] = '\\';
79       j++;
80     }
81     if(bufin[i] == '\n') {
82       if(man) {
83 	// skip LF for man page
84 	i++;
85       } else {
86 	// keep LF
87 	bufout[j] = bufin[i];
88 	i++;
89 	j++;
90       }
91     } else {
92       // standard char
93       bufout[j] = bufin[i];
94       i++;
95       j++;
96     }
97   }
98   bufout[j] = '\0';
99   printf("%s", bufout);
100   if(man) {
101     printf(" ");
102   }
103 }
104 
105 // escape only
qs_man_println(int man,const char * fmt,...)106 void qs_man_println(int man, const char *fmt, ...) {
107   char bufin[4096];
108   char bufout[4096];
109   va_list args;
110   int i = 0;
111   int j = 0;
112   memset(bufin, 0, 4096);
113   va_start(args, fmt);
114   vsprintf(bufin, fmt, args);
115   while(bufin[i] && j < 4000) {
116     // escape "\\" and "-" for man page
117     if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
118       bufout[j] = '\\';
119       j++;
120     }
121     // standard char
122     bufout[j] = bufin[i];
123     i++;
124     j++;
125   }
126   bufout[j] = '\0';
127   printf("%s", bufout);
128 }
129 
qs_CMD(const char * cmd)130 char *qs_CMD(const char *cmd) {
131   char *buf = calloc(1024, 1);
132   int i = 0;
133   while(cmd[i] && i < 1023) {
134     buf[i] = toupper(cmd[i]);
135     i++;
136   }
137   buf[i] = '\0';
138   return buf;
139 }
140 
141 /* io --------------------------------------------------------- */
142 /*
143  * reads a line from stdin
144  *
145  * @param s Buffer to write line to
146  * @param n Length of the buffer
147  * @return 0 on EOF, or 1 if there is more data to read
148  */
qs_getLine(char * s,int n)149 int qs_getLine(char *s, int n) {
150   int i = 0;
151   while (1) {
152     s[i] = (char)getchar();
153     if(s[i] == EOF) return 0;
154     if (s[i] == CR) {
155       s[i] = getchar();
156     }
157     if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
158       s[i] = '\0';
159       return 1;
160     }
161     ++i;
162   }
163 }
164 
165 /*
166  * reads a line from file
167  *
168  * @param s Buffer to write line to
169  * @param n Length of the buffer
170  * @return 0 on EOF, or 1 if there is more data to read
171  */
qs_getLinef(char * s,int n,FILE * f)172 int qs_getLinef(char *s, int n, FILE *f) {
173   register int i = 0;
174   while (1) {
175     s[i] = (char) fgetc(f);
176     if (s[i] == CR) {
177       s[i] = fgetc(f);
178     }
179     if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
180       s[i] = '\0';
181       return (feof(f) ? 1 : 0);
182     }
183     ++i;
184   }
185 }
186 
187 /* time ------------------------------------------------------- */
188 /*
189  * We implement our own time which is either
190  * the system time (real time) or the time from
191  * the access log lines (offline) if m_qs_offline
192  * has been set (use qs_set2OfflineMode() to enable
193  * the offline mode).
194  *
195  * @param tme Set to the time since the Epoch in seconds.
196  */
qs_time(time_t * tme)197 void qs_time(time_t *tme) {
198   if(m_qs_offline) {
199     /* use virtual time from the access log */
200     *tme = m_qs_virtualSystemTime;
201   } else {
202     time(tme);
203   }
204 }
205 
206 /**
207  * Sets time measurement (qs_time()) to offline mode.
208  */
qs_set2OfflineMode()209 void qs_set2OfflineMode() {
210   m_qs_offline = 1;
211 }
212 
213 /*
214  * Updates the virtual time.
215  */
qs_setTime(time_t tme)216 void qs_setTime(time_t tme) {
217   m_qs_virtualSystemTime = tme;
218 }
219 
220 /* synchronisation -------------------------------------------- */
221 /*
222  * locks all counter
223  */
qs_csLock()224 void qs_csLock() {
225   pthread_mutex_lock(&m_qs_lock_cs);
226 }
227 
228 /*
229  * unlocks all counter
230  */
qs_csUnLock()231 void qs_csUnLock() {
232   pthread_mutex_unlock(&m_qs_lock_cs);
233 }
234 
235 /*
236  * init locks
237  */
qs_csInitLock()238 void qs_csInitLock() {
239   pthread_mutex_init(&m_qs_lock_cs, NULL);
240 }
241 
242 /* logs ------------------------------------------------------- */
243 
244 /**
245  * Keeps only the specified number of files
246  *
247  * @param file_name Absolute file name
248  * @param generations Number of files to keep
249  */
qs_deleteOldFiles(const char * file_name,int generations)250 void qs_deleteOldFiles(const char *file_name, int generations) {
251   DIR *dir;
252   char dirname[QS_HUGE_STR];
253   char *p;
254   memset(dirname, 0, QS_HUGE_STR);
255   if(strlen(file_name) > (QS_HUGE_STR - 12)) {
256     // invalid file length
257     return;
258   }
259   if(strrchr(file_name, '/') == NULL) {
260     sprintf(dirname, "./%s", file_name);
261   } else {
262     strcpy(dirname, file_name);
263   }
264   p = strrchr(dirname, '/');
265   p[0] = '\0'; p++;
266   dir = opendir(dirname);
267   if(dir) {
268     int num = 0;
269     struct dirent *de;
270     char filename[QS_HUGE_STR];
271     snprintf(filename, sizeof(filename), "%s.20", p);
272     /* determine how many files to delete */
273     while((de = readdir(dir)) != 0) {
274       if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
275 	num++;
276       }
277     }
278     /* delete the oldest files (assumes they are ordered by their creation date) */
279     while(num > generations) {
280       char old[QS_HUGE_STR];
281       old[0] = '\0';
282       rewinddir(dir);
283       while((de = readdir(dir)) != 0) {
284 	if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
285 	  if(strcmp(old, de->d_name) > 0) {
286 	    snprintf(old, sizeof(old), "%s", de->d_name);
287 	  } else {
288 	    if(old[0] == '\0') {
289 	      snprintf(old, sizeof(old), "%s", de->d_name);
290 	    }
291 	  }
292 	}
293       }
294       {
295 	/* build abs path and delete it */
296 	char unl[QS_HUGE_STR];
297 	snprintf(unl, sizeof(unl), "%s/%s", dirname, old);
298 	unlink(unl);
299       }
300       num--;
301     }
302     closedir(dir);
303   }
304 }
305 
306 /* user ------------------------------------------------------- */
qs_setuid(const char * username,const char * cmd)307 void qs_setuid(const char *username, const char *cmd) {
308   if(username && getuid() == 0) {
309     struct passwd *pwd = getpwnam(username);
310     uid_t uid, gid;
311     if(pwd == NULL) {
312       fprintf(stderr, "[%s] failed to switch user: unknown user id '%s'\n", cmd, username);
313       exit(1);
314     }
315     uid = pwd->pw_uid;
316     gid = pwd->pw_gid;
317     setgid(gid);
318     setuid(uid);
319     if(getuid() != uid) {
320       fprintf(stderr, "[%s] setuid failed (%s,%d)\n", cmd, username, uid);
321       exit(1);
322     }
323     if(getgid() != gid) {
324       fprintf(stderr, "[%s] setgid failed (%d)\n", cmd, gid);
325       exit(1);
326     }
327   }
328 }
329