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