summaryrefslogtreecommitdiffstats
path: root/tools/src/qs_util.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-17 03:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-17 03:51:28 +0000
commit2b07c041cb218eca6e548bac9c4347f8a90c474c (patch)
tree679142f3916fa927903c6f245896f5c0325a3254 /tools/src/qs_util.c
parentInitial commit. (diff)
downloadlibapache2-mod-qos-2b07c041cb218eca6e548bac9c4347f8a90c474c.tar.xz
libapache2-mod-qos-2b07c041cb218eca6e548bac9c4347f8a90c474c.zip
Adding upstream version 11.74.upstream/11.74upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/src/qs_util.c')
-rw-r--r--tools/src/qs_util.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/tools/src/qs_util.c b/tools/src/qs_util.c
new file mode 100644
index 0000000..ddf1e89
--- /dev/null
+++ b/tools/src/qs_util.c
@@ -0,0 +1,409 @@
+/**
+ * Utilities for the quality of service module mod_qos.
+ *
+ * See http://mod-qos.sourceforge.net/ for further
+ * details.
+ *
+ * Copyright (C) 2023 Pascal Buchbinder
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+static const char revision[] = "$Id: qs_util.c 2654 2022-05-13 09:12:42Z pbuchbinder $";
+
+#include <stdio.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include <pcre2.h>
+typedef pcre2_match_data* match_data_pt;
+typedef size_t* match_vector_pt;
+
+#include "qs_util.h"
+
+/* mutex for counter access */
+static pthread_mutex_t m_qs_lock_cs;
+/* online/offline mode */
+static int m_qs_offline = 0;
+/* internal clock for offline analysis
+ * stores time in seconds */
+static time_t m_qs_virtualSystemTime = 0;
+
+/* ----------------------------------
+ * functions
+ * ---------------------------------- */
+
+/**
+ * man:
+ * - escape special chars, like "\" and "-"
+ * - wipe leading spaces
+ * - wipe tailing LF
+ */
+void qs_man_print(int man, const char *fmt, ...) {
+ char bufin[4096];
+ char bufout[4096];
+ va_list args;
+ int i = 0;
+ int j = 0;
+ memset(bufin, 0, 4096);
+ va_start(args, fmt);
+ vsprintf(bufin, fmt, args);
+ if(man) {
+ // wipe leading spaces
+ // while(bufin[i] == ' ' && bufin[i+1] == ' ') {
+ while(bufin[i] == ' ') {
+ i++;
+ }
+ }
+ while(bufin[i] && j < 4000) {
+ // escape "\\" and "-" for man page
+ if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
+ bufout[j] = '\\';
+ j++;
+ }
+ if(bufin[i] == '\n') {
+ if(man) {
+ // skip LF for man page
+ i++;
+ } else {
+ // keep LF
+ bufout[j] = bufin[i];
+ i++;
+ j++;
+ }
+ } else {
+ // standard char
+ bufout[j] = bufin[i];
+ i++;
+ j++;
+ }
+ }
+ bufout[j] = '\0';
+ printf("%s", bufout);
+ if(man) {
+ printf(" ");
+ }
+}
+
+// escape only
+void qs_man_println(int man, const char *fmt, ...) {
+ char bufin[4096];
+ char bufout[4096];
+ va_list args;
+ int i = 0;
+ int j = 0;
+ memset(bufin, 0, 4096);
+ va_start(args, fmt);
+ vsprintf(bufin, fmt, args);
+ while(bufin[i] && j < 4000) {
+ // escape "\\" and "-" for man page
+ if(man && (bufin[i] == '\\' || bufin[i] == '-')) {
+ bufout[j] = '\\';
+ j++;
+ }
+ // standard char
+ bufout[j] = bufin[i];
+ i++;
+ j++;
+ }
+ bufout[j] = '\0';
+ printf("%s", bufout);
+}
+
+char *qs_CMD(const char *cmd) {
+ char *buf = calloc(1024, 1);
+ int i = 0;
+ while(cmd[i] && i < 1023) {
+ buf[i] = toupper(cmd[i]);
+ i++;
+ }
+ buf[i] = '\0';
+ return buf;
+}
+
+/* io --------------------------------------------------------- */
+/*
+ * reads a line from stdin
+ *
+ * @param s Buffer to write line to
+ * @param n Length of the buffer
+ * @return 0 on EOF, or 1 if there is more data to read
+ */
+int qs_getLine(char *s, int n) {
+ int i = 0;
+ while (1) {
+ s[i] = (char)getchar();
+ if(s[i] == EOF) return 0;
+ if (s[i] == CR) {
+ s[i] = getchar();
+ }
+ if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+ s[i] = '\0';
+ return 1;
+ }
+ ++i;
+ }
+}
+
+/*
+ * reads a line from file
+ *
+ * @param s Buffer to write line to
+ * @param n Length of the buffer
+ * @return 0 on EOF, or 1 if there is more data to read
+ */
+int qs_getLinef(char *s, int n, FILE *f) {
+ register int i = 0;
+ while (1) {
+ s[i] = (char) fgetc(f);
+ if (s[i] == CR) {
+ s[i] = fgetc(f);
+ }
+ if ((s[i] == 0x4) || (s[i] == LF) || (i == (n - 1))) {
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ ++i;
+ }
+}
+
+/* time ------------------------------------------------------- */
+/*
+ * We implement our own time which is either
+ * the system time (real time) or the time from
+ * the access log lines (offline) if m_qs_offline
+ * has been set (use qs_set2OfflineMode() to enable
+ * the offline mode).
+ *
+ * @param tme Set to the time since the Epoch in seconds.
+ */
+void qs_time(time_t *tme) {
+ if(m_qs_offline) {
+ /* use virtual time from the access log */
+ *tme = m_qs_virtualSystemTime;
+ } else {
+ time(tme);
+ }
+}
+
+/**
+ * Sets time measurement (qs_time()) to offline mode.
+ */
+void qs_set2OfflineMode() {
+ m_qs_offline = 1;
+}
+
+/*
+ * Updates the virtual time.
+ */
+void qs_setTime(time_t tme) {
+ m_qs_virtualSystemTime = tme;
+}
+
+/* synchronisation -------------------------------------------- */
+/*
+ * locks all counter
+ */
+void qs_csLock() {
+ pthread_mutex_lock(&m_qs_lock_cs);
+}
+
+/*
+ * unlocks all counter
+ */
+void qs_csUnLock() {
+ pthread_mutex_unlock(&m_qs_lock_cs);
+}
+
+/*
+ * init locks
+ */
+void qs_csInitLock() {
+ pthread_mutex_init(&m_qs_lock_cs, NULL);
+}
+
+/* logs ------------------------------------------------------- */
+
+/**
+ * Keeps only the specified number of files
+ *
+ * @param file_name Absolute file name
+ * @param generations Number of files to keep
+ */
+void qs_deleteOldFiles(const char *file_name, int generations) {
+ DIR *dir;
+ char dirname[QS_HUGE_STR];
+ char *p;
+ memset(dirname, 0, QS_HUGE_STR);
+ if(strlen(file_name) > (QS_HUGE_STR - 12)) {
+ // invalid file length
+ return;
+ }
+ if(strrchr(file_name, '/') == NULL) {
+ sprintf(dirname, "./%s", file_name);
+ } else {
+ strcpy(dirname, file_name);
+ }
+ p = strrchr(dirname, '/');
+ p[0] = '\0'; p++;
+ dir = opendir(dirname);
+ if(dir) {
+ int num = 0;
+ struct dirent *de;
+ char filename[QS_HUGE_STR];
+ snprintf(filename, sizeof(filename), "%s.20", p);
+ /* determine how many files to delete */
+ while((de = readdir(dir)) != 0) {
+ if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
+ num++;
+ }
+ }
+ /* delete the oldest files (assumes they are ordered by their creation date) */
+ while(num > generations) {
+ char old[QS_HUGE_STR];
+ old[0] = '\0';
+ rewinddir(dir);
+ while((de = readdir(dir)) != 0) {
+ if(de->d_name && (strncmp(de->d_name, filename, strlen(filename)) == 0)) {
+ if(strcmp(old, de->d_name) > 0) {
+ snprintf(old, sizeof(old), "%s", de->d_name);
+ } else {
+ if(old[0] == '\0') {
+ snprintf(old, sizeof(old), "%s", de->d_name);
+ }
+ }
+ }
+ }
+ {
+ /* build abs path and delete it */
+ char unl[QS_HUGE_STR];
+ snprintf(unl, sizeof(unl), "%s/%s", dirname, old);
+ unlink(unl);
+ }
+ num--;
+ }
+ closedir(dir);
+ }
+}
+
+/* user ------------------------------------------------------- */
+void qs_setuid(const char *username, const char *cmd) {
+ if(username && getuid() == 0) {
+ struct passwd *pwd = getpwnam(username);
+ uid_t uid, gid;
+ if(pwd == NULL) {
+ fprintf(stderr, "[%s] failed to switch user: unknown user id '%s'\n", cmd, username);
+ exit(1);
+ }
+ uid = pwd->pw_uid;
+ gid = pwd->pw_gid;
+ setgid(gid);
+ setuid(uid);
+ if(getuid() != uid) {
+ fprintf(stderr, "[%s] setuid failed (%s,%d)\n", cmd, username, uid);
+ exit(1);
+ }
+ if(getgid() != gid) {
+ fprintf(stderr, "[%s] setgid failed (%d)\n", cmd, gid);
+ exit(1);
+ }
+ }
+}
+/* pcre ------------------------------------------------------- */
+int qs_pregfree(void *p) {
+ qs_regfree((qs_regex_t *)p);
+ return 0;
+}
+
+void qs_regfree(qs_regex_t *preg) {
+ if(preg->state == 1) {
+ pcre2_code_free(preg->re_pcre);
+ }
+}
+
+int qs_regcomp(qs_regex_t *preg, const char *pattern, int cflags) {
+ unsigned int capcount;
+ size_t erroffset;
+ int errcode = 0;
+ int options = cflags;
+ preg->state = 0;
+
+ preg->re_pcre = pcre2_compile((const unsigned char *)pattern,
+ PCRE2_ZERO_TERMINATED, options, &errcode,
+ &erroffset, NULL);
+
+ if (preg->re_pcre == NULL) {
+ return 1;
+ }
+
+ pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
+ PCRE2_INFO_CAPTURECOUNT, &capcount);
+ preg->re_nsub = capcount;
+
+ preg->state = 1;
+
+ return 0;
+}
+
+int qs_regexec_len(const qs_regex_t *preg, const char *buff,
+ unsigned int len, unsigned int nmatch,
+ qs_regmatch_t *pmatch, int eflags) {
+ int rc;
+ int options = 0;
+ match_vector_pt ovector = NULL;
+ unsigned int ncaps = (unsigned int)preg->re_nsub + 1;
+ match_data_pt data = pcre2_match_data_create(ncaps, NULL);
+
+ if (!data) {
+ return -1;
+ }
+
+ options = eflags;
+
+ rc = pcre2_match((const pcre2_code *)preg->re_pcre,
+ (const unsigned char *)buff, len,
+ 0, options, data, NULL);
+ ovector = pcre2_get_ovector_pointer(data);
+
+ if (rc >= 0) {
+ unsigned int n = rc, i;
+ if (n == 0 || n > nmatch)
+ rc = n = nmatch; /* All capture slots were filled in */
+ for (i = 0; i < n; i++) {
+ pmatch[i].rm_so = ovector[i * 2];
+ pmatch[i].rm_eo = ovector[i * 2 + 1];
+ }
+ for (; i < nmatch; i++) {
+ pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ }
+ pcre2_match_data_free(data);
+ return rc;
+ }
+ else {
+ pcre2_match_data_free(data);
+
+ return -1;
+ }
+}