Compare commits

..
27 Commits
23 .. 31
Author SHA1 Message Date
Ikey Doherty 02ece110b5 Release v31
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:29:35 +01:00
Ikey Doherty 3a6bf4a167 Fix print statements when a dir cannot be created
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:20:31 +01:00
Ikey Doherty fc61591870 Fix distcheck
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:16:53 +01:00
Ikey Doherty 2f26fc55c7 Stick myself in authors
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:14:47 +01:00
Ikey Doherty dfff530803 Address unused return issues
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:13:41 +01:00
Ikey Doherty 9685fcb632 Fix some warnings
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:07:57 +01:00
Ikey Doherty 14495e064b Make use of autofree to simplify exit conditions
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 17:03:54 +01:00
Ikey Doherty 7b64fb601d server: Forcibly inline functions
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 16:41:17 +01:00
Ikey Doherty b24d93fe3d server: Atomically manage a maximum connection ceiling
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 16:32:01 +01:00
Ikey Doherty 5b7c484b64 Fix up some warnings
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-17 15:17:05 +01:00
Ikey Doherty d23b8e17bd fuse: Greatly simplify allocation and management of strings
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 17:08:00 +01:00
Ikey Doherty b120d1c38f Fix last of the mkdir users
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:54:09 +01:00
Ikey Doherty bbc34ad790 Get rid of mkdir calls
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:50:27 +01:00
Ikey Doherty caf84d9b5f Use consistent file headers
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:43:09 +01:00
Ikey Doherty bc276b4920 Incorporate the nica files component
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:42:41 +01:00
Ikey Doherty 78485dc583 Take care of unused variables
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:37:52 +01:00
Ikey Doherty ce02c64c0c Drop glib entirely
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:34:21 +01:00
Ikey Doherty f272c08d4f Use a hashmap to maintain memory
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:30:17 +01:00
Ikey Doherty 3babbe4b7b Incorporate portions of libnica
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 16:18:38 +01:00
Ikey Doherty aad87bbdc1 Use curly braces everywhere
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:48:34 +01:00
Ikey Doherty 4505d32c68 Fix last commit
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:35:01 +01:00
Ikey Doherty 67a9735acb Swap GMutex for pthread_mutex_t
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:33:37 +01:00
Ikey Doherty 1c043d5bd7 Add strict flags
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:22:21 +01:00
Ikey Doherty 8c01f4012f Apply clang-format to codebase (switching to spaces for maintainence)
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:20:29 +01:00
Ikey Doherty 955c1b7e99 Incorporate clang-format helpers (3.8)
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:20:12 +01:00
Ikey Doherty a92e6f386a Add an autogen script
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-11 15:17:46 +01:00
Arjan van de Ven 670069d659 version 30 2016-02-01 14:34:27 -05:00
15 changed files with 2249 additions and 842 deletions
+84
View File
@@ -0,0 +1,84 @@
---
AccessModifierOffset: 0
AlignAfterOpenBracket: true
AlignConsecutiveAssignments: false
#uncomment for clang 3.9
#AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
# AlwaysBreakAfterDefinitionReturnType: None
#uncomment for clang 3.9
#AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: true
# BraceWrapping: (not set since BreakBeforeBraces is not Custom)
BreakBeforeBinaryOperators: None
# BreakAfterJavaFieldAnnotations: (not java)
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
#uncomment for clang 3.9
#BreakStringLiterals: false
ColumnLimit: 100
CommentPragmas: '\*\<'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ NC_LIST_FOREACH ]
#Uncomment for clang 3.9
#IncludeCategories:
# - Regex: '^"'
# Priority: 1
# IncludeIsMainRegex: (project doesn't use a main includes that can add other includes via regex)
IndentCaseLabels: false
IndentWidth: 8
IndentWrappedFunctionNames: false
# JavaScriptQuotes: (not javascript)
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
# ObjCBlockIndentWidth: (not objc)
# ObjCSpaceAfterProperty: (not objc)
# ObjCSpaceBeforeProtocolList: (not objc)
PenaltyBreakBeforeFirstCallParameter: 400
PenaltyBreakComment: 0
# PenaltyBreakFirstLessLess: (not cpp)
PenaltyBreakString: 500
PenaltyExcessCharacter: 10000
PenaltyReturnTypeOnItsOwnLine: 600
PointerAlignment: Right
#uncomment for clang 3.9
#ReflowComments: true
#uncomment for clang 3.9
#SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
# SpacesInContainerLiterals: (not objc or javascript)
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...
+7
View File
@@ -22,3 +22,10 @@ stamp-h1
*.tar.gz *.tar.gz
tags tags
cscope.* cscope.*
*.la
*.lo
.libs/
config.guess
config.sub
libtool
ltmain.sh
+28 -4
View File
@@ -4,19 +4,43 @@ DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) \ --with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) \
--with-systemdtmpfilesdir=$$dc_install_base/$(tmpfilesdir) --with-systemdtmpfilesdir=$$dc_install_base/$(tmpfilesdir)
AM_CFLAGS = \
-fstack-protector -Wall -pedantic \
-Wstrict-prototypes -Wundef -fno-common \
-Werror-implicit-function-declaration \
-Wformat -Wformat-security -Werror=format-security \
-Wno-conversion -Wunused-variable -Wunreachable-code \
-Wall -W -D_FORTIFY_SOURCE=2 -std=c11
AM_CPPFLAGS = $(AM_CFLAGS) ${curl_CFLAGS} ${fuse_CFLAGS}
bin_PROGRAMS = clr_debug_fuse clr_debug_daemon clr_debug_prepare bin_PROGRAMS = clr_debug_fuse clr_debug_daemon clr_debug_prepare
noinst_LTLIBRARIES = \
libnica.la
# Extract of libnica components
libnica_la_SOURCES = \
src/nica/files.c \
src/nica/files.h \
src/nica/hashmap.c \
src/nica/hashmap.h \
src/nica/util.h
clr_debug_fuse_SOURCES = src/fuse.c src/client.c clr_debug_fuse_SOURCES = src/fuse.c src/client.c
clr_debug_daemon_SOURCES = src/server.c clr_debug_daemon_SOURCES = src/server.c
clr_debug_daemon_CFLAGS = \
-pthread \
$(AM_CFLAGS)
clr_debug_prepare_SOURCES = src/prepare.c clr_debug_prepare_SOURCES = src/prepare.c
AM_CPPFLAGS = $(AM_CFLAGS) $(glib_CFLAGS) ${curl_CFLAGS} ${fuse_CFLAGS} clr_debug_fuse_LDADD = ${fuse_LIBS} libnica.la
clr_debug_fuse_LDADD = ${fuse_LIBS} $(glib_LIBS) clr_debug_daemon_LDADD = ${curl_LIBS} libnica.la
clr_debug_daemon_LDADD = $(glib_LIBS) ${curl_LIBS} clr_debug_prepare_LDADD = libnica.la
clr_debug_prepare_LDADD = $(glib_LIBS)
systemdsystemunit_DATA = clr_debug_fuse.service clr_debug_daemon.service systemdsystemunit_DATA = clr_debug_fuse.service clr_debug_daemon.service
Executable
+14
View File
@@ -0,0 +1,14 @@
#!/bin/sh
set -e
autoreconf --force --install --symlink --warnings=all
args="\
--sysconfdir=/etc \
--localstatedir=/var \
--prefix=/usr \
--enable-silent-rules"
./configure CFLAGS="-g -O1 $CFLAGS" $args "$@"
make clean
+2 -2
View File
@@ -2,16 +2,16 @@
# Process this file with autoconf to produce a configure script. # Process this file with autoconf to produce a configure script.
AC_PREREQ([2.66]) AC_PREREQ([2.66])
AC_INIT(clr-debug-info, 23, arjan@linux.intel.com) AC_INIT(clr-debug-info, 31, arjan@linux.intel.com)
AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects]) AM_INIT_AUTOMAKE([foreign -Wall -W subdir-objects])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_PROG_CC AC_PROG_CC
AC_LANG(C) AC_LANG(C)
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
PKG_CHECK_MODULES([glib], [glib-2.0 gthread-2.0])
PKG_CHECK_MODULES([curl], [libcurl]) PKG_CHECK_MODULES([curl], [libcurl])
PKG_CHECK_MODULES([fuse], [fuse]) PKG_CHECK_MODULES([fuse], [fuse])
PKG_CHECK_MODULES([SYSTEMD], [systemd]) PKG_CHECK_MODULES([SYSTEMD], [systemd])
LT_INIT
dir="" dir=""
AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
+66 -60
View File
@@ -18,21 +18,23 @@
* *
* Authors: * Authors:
* Arjan van de Ven <arjanvandeven@gmail.com> * Arjan van de Ven <arjanvandeven@gmail.com>
* Ikey Doherty <michael.i.doherty@intel.com>
* *
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include "nica/util.h"
#include <errno.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <time.h> #include <time.h>
#include <unistd.h>
/* 0.75 seconds timeout */ /* 0.75 seconds timeout */
#define TIMEOUT 750000 #define TIMEOUT 750000
@@ -45,71 +47,75 @@ time_t deadtime;
void try_to_get(const char *path, int pid, time_t timestamp) void try_to_get(const char *path, int pid, time_t timestamp)
{ {
int sockfd; int sockfd;
struct sockaddr_un sun; struct sockaddr_un sun;
int ret; int ret;
socklen_t len; socklen_t len;
char *command; char *command;
fd_set rfds; fd_set rfds;
struct timeval tv; struct timeval tv;
struct ucred cred; struct ucred cred;
int shorttime = 0; int shorttime = 0;
__nc_unused__ size_t wr = -1;
// printf("Trying to aquire %s\n", path); // printf("Trying to aquire %s\n", path);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) if (sockfd < 0) {
return; return;
}
sun.sun_family = AF_UNIX; sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, ":clr-debug-info"); strcpy(sun.sun_path, ":clr-debug-info");
sun.sun_path[0] = 0; /* anonymous unix socket */ sun.sun_path[0] = 0; /* anonymous unix socket */
ret = connect(sockfd, (struct sockaddr *)&sun, offsetof(struct sockaddr_un, sun_path) + strlen(":clr-debug-info") + 1); ret = connect(sockfd,
if (ret < 0) { (struct sockaddr *)&sun,
printf("Cannot connect %s\n", strerror(errno)); offsetof(struct sockaddr_un, sun_path) + strlen(":clr-debug-info") + 1);
close(sockfd); if (ret < 0) {
return; printf("Cannot connect %s\n", strerror(errno));
} close(sockfd);
return;
}
len = sizeof(cred); len = sizeof(cred);
memset(&cred, 0, sizeof(cred)); memset(&cred, 0, sizeof(cred));
ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len); ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len);
if (ret == 0 && cred.pid == pid) { if (ret == 0 && cred.pid == pid) {
printf("Recursion\n"); printf("Recursion\n");
close(sockfd); close(sockfd);
} }
command = NULL; command = NULL;
if (asprintf(&command, "%llu:%s:%s", (unsigned long long)timestamp, prefix, path) < 0) { if (asprintf(&command, "%llu:%s:%s", (unsigned long long)timestamp, prefix, path) < 0) {
close(sockfd); close(sockfd);
return; return;
} }
write(sockfd, command, strlen(command )+1); wr = write(sockfd, command, strlen(command) + 1);
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_SET(sockfd, &rfds); FD_SET(sockfd, &rfds);
/* Wait up to 0.5 seconds. */ /* Wait up to 0.5 seconds. */
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = TIMEOUT; tv.tv_usec = TIMEOUT;
/* if we had a timeout recently, must go quicker to avoid sequential delays */ /* if we had a timeout recently, must go quicker to avoid sequential delays */
if (deadtime > time(NULL)) { if (deadtime > time(NULL)) {
tv.tv_usec = TIMEOUT2; tv.tv_usec = TIMEOUT2;
shorttime = 1; shorttime = 1;
} }
ret = 1; ret = 1;
if (!timestamp) {
ret = select(sockfd + 1, &rfds, NULL, &rfds, &tv);
}
if (ret == 0 && !shorttime) {
printf("timeout for %s\n", path);
deadtime = time(NULL) + 4;
}
if (!timestamp) close(sockfd);
ret = select(sockfd+1, &rfds, NULL, &rfds, &tv);
if (ret == 0 && !shorttime) {
printf("timeout for %s\n", path);
deadtime = time(NULL) + 4;
}
close(sockfd);
} }
+416 -390
View File
File diff suppressed because it is too large Load Diff
+262
View File
@@ -0,0 +1,262 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Authors:
*
* Ikey Doherty <michael.i.doherty@intel.com>
*/
#define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <ftw.h>
#include <libgen.h>
#include <stdarg.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include "files.h"
#include "util.h"
#define COPY_BUFFER_SIZE 8192
DEF_AUTOFREE(DIR, closedir)
bool nc_file_exists(const char *path)
{
struct stat st = {.st_ino = 0 };
return (lstat(path, &st) == 0);
}
char *nc_get_file_parent(const char *path)
{
autofree(char) *d = strdup(path);
char *r = realpath(dirname(d), NULL);
return r;
}
bool nc_mkdir_p(const char *path, mode_t mode)
{
autofree(char) *cl = NULL;
char *cl_base = NULL;
if (streq(path, ".") || streq(path, "/") || streq(path, "//")) {
return true;
}
cl = strdup(path);
cl_base = dirname(cl);
if (!nc_mkdir_p(cl_base, mode) && errno != EEXIST) {
return false;
}
return !((mkdir(path, mode) < 0 && errno != EEXIST));
}
/**
* nftw callback
*/
static int _rm_rf(const char *path, __attribute__((unused)) const struct stat *sb, int typeflag,
__attribute__((unused)) struct FTW *ftwbuf)
{
if (typeflag == FTW_F) {
return unlink(path);
}
return rmdir(path);
}
bool nc_rm_rf(const char *path)
{
bool ret = nftw(path, _rm_rf, 35, FTW_DEPTH) == 0;
sync();
return ret;
}
bool nc_copy_file(const char *src, const char *dst, mode_t mode, bool remove_target)
{
int src_fd = -1;
int dest_fd = -1;
bool ret = true;
char buffer[COPY_BUFFER_SIZE] = { 0 };
int r = 0;
if (remove_target) {
if (nc_file_exists(dst) && unlink(dst) < 0) {
return false;
}
sync();
}
src_fd = open(src, O_RDONLY);
if (src_fd < 0) {
return false;
}
dest_fd = open(dst, O_WRONLY | O_TRUNC | O_APPEND | O_CREAT, mode);
if (dest_fd < 0) {
ret = false;
goto end;
}
while (true) {
if ((r = read(src_fd, &buffer, sizeof(buffer))) < 0) {
ret = false;
break;
}
if (write(dest_fd, buffer, sizeof(buffer)) != r) {
break;
}
}
ret = true;
end:
if (src_fd >= 0) {
close(src_fd);
}
if (dest_fd >= 0) {
close(dest_fd);
}
if (remove_target) {
sync();
}
return ret;
}
/*
* The following functions are taken from Ikey Doherty's goofiboot
* project
*/
char *nc_build_case_correct_path_va(const char *c, va_list ap)
{
char *p = NULL;
char *root = NULL;
struct stat st = {.st_ino = 0 };
struct dirent *ent = NULL;
p = (char *)c;
while (p) {
char *t = NULL;
char *sav = NULL;
autofree(DIR) *dirn = NULL;
if (!root) {
root = strdup(p);
} else {
sav = strdup(root);
if (!asprintf(&t, "%s/%s", root, p)) {
fprintf(stderr,
"nc_build_case_correct_path_va: Out "
"of memory\n");
va_end(ap);
if (sav) {
free(sav);
}
return NULL;
}
free(root);
root = t;
t = NULL;
}
autofree(char) *dirp = strdup(root);
char *dir = dirname(dirp);
if (stat(dir, &st) != 0) {
goto clean;
}
if (!S_ISDIR(st.st_mode)) {
goto clean;
}
/* Iterate the directory and find the case insensitive name,
* using
* this if it exists. Otherwise continue with the non existent
* path
*/
dirn = opendir(dir);
if (!dirn) {
goto clean;
}
while ((ent = readdir(dirn)) != NULL) {
if (strncmp(ent->d_name, ".", 1) == 0 ||
strncmp(ent->d_name, "..", 2) == 0) {
continue;
}
if (strcasecmp(ent->d_name, p) == 0) {
if (sav) {
if (!asprintf(&t, "%s/%s", sav, ent->d_name)) {
fprintf(stderr,
"nc_build_case_"
"correct_path_va: Out "
"of memory\n");
return NULL;
}
free(root);
root = t;
t = NULL;
} else {
if (root) {
sav = strdup(root);
free(root);
root = NULL;
if (!sav) {
fprintf(stderr,
"nc_build_"
"case_correct_"
"path_va: Out "
"of memory\n");
return NULL;
}
}
}
break;
}
}
clean:
if (sav) {
free(sav);
}
p = va_arg(ap, char *);
}
return root;
}
char *nc_build_case_correct_path(const char *c, ...)
{
va_list ap;
char *ret = NULL;
va_start(ap, c);
ret = nc_build_case_correct_path_va(c, ap);
va_end(ap);
return ret;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+95
View File
@@ -0,0 +1,95 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Authors:
*
* Ikey Doherty <michael.i.doherty@intel.com>
*/
#pragma once
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include "util.h"
/**
* Determine whether the given path exists in the filesystem
*
* @param path Path to the file we should check
*/
bool nc_file_exists(const char *path);
/**
* Return the parent path for a given file
*
* @note This is an allocated string, and must be freed by the caller
* @param p Path to file
* @return a newly allocated string
*/
char *nc_get_file_parent(const char *path);
/**
* Recursively make the directories for the given path
*
* @param path Path to directory to create
* @param mode Mode to create new directories with
*
* @return A boolean value, indicating success or failure
*/
bool nc_mkdir_p(const char *path, mode_t mode);
/**
* Recursively remove the tree at @path
*
* @param path The path to remove (and all children)
*
* @return A boolean value, indicating success or failure
*/
bool nc_rm_rf(const char *path);
/**
* Simple utility to copy path @src to path @dst, with mode @mode
*
* @note This will truncate the target if it exists, and does
* not preserve stat information, when @remove_target is set to true
*
* @param src Path to the source file
* @param dst Path to the destination file
* @param mode Mode of the new file when creating
* @param remove_target Whether to remove an existing target file first
*
* @return True if this succeeded
*/
bool nc_copy_file(const char *src, const char *dst, mode_t mode, bool remove_target);
/**
* Gradually build up a valid path, which may point within an existing
* tree, to mitigate any case sensitivity issues on case-preserving
* filesystems
*/
__attribute__((sentinel(0))) char *nc_build_case_correct_path(const char *c, ...);
char *nc_build_case_correct_path_va(const char *c, va_list ap);
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+491
View File
@@ -0,0 +1,491 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Authors:
*
* Ikey Doherty <michael.i.doherty@intel.com>
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hashmap.h"
#define INITIAL_SIZE 61
/* When we're "full", 70% */
#define FULL_FACTOR 0.7
/* Multiple to increase bucket size by */
#define INCREASE_FACTOR 4
/**
* An bucket/chain within the hashmap
*/
typedef struct NcHashmapEntry {
void *hash; /**<The key for this item */
void *value; /**<Value for this item */
struct NcHashmapEntry *next; /**<Next item in the chain */
bool occ; /**<Whether this bucket is occupied */
} NcHashmapEntry;
/**
* A NcHashmap
*/
struct NcHashmap {
int size; /**<Current size of the hashmap */
int next_size; /**<Next size at which we need to resize */
int n_buckets; /**<Current number of buckets */
NcHashmapEntry *buckets; /**<Stores our bucket chains */
nc_hash_create_func hash; /**<Hash generation function */
nc_hash_compare_func compare; /**<Key comparison function */
nc_hash_free_func key_free; /**<Cleanup function for keys */
nc_hash_free_func value_free; /**<Cleanup function for values */
};
/**
* Iteration object
*/
typedef struct _NcHashmapIter {
int bucket; /**<Current bucket position */
NcHashmap *map; /**<Associated NcHashmap */
void *item; /**<Current item in this iteration */
} _NcHashmapIter;
static void nc_hashmap_update_next_size(NcHashmap *self);
static bool nc_hashmap_resize(NcHashmap *self);
static inline bool nc_hashmap_maybe_resize(NcHashmap *self)
{
if (!self) {
return false;
}
if (self->size >= self->next_size) {
return true;
}
return false;
}
static NcHashmap *nc_hashmap_new_internal(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
NcHashmap *map = NULL;
NcHashmapEntry *buckets = NULL;
map = calloc(1, sizeof(NcHashmap));
if (!map) {
return NULL;
}
buckets = calloc(INITIAL_SIZE, sizeof(NcHashmapEntry));
if (!buckets) {
free(map);
return NULL;
}
map->buckets = buckets;
map->n_buckets = INITIAL_SIZE;
map->hash = create ? create : nc_simple_hash;
map->compare = compare ? compare : nc_simple_compare;
map->key_free = key_free;
map->value_free = value_free;
map->size = 0;
nc_hashmap_update_next_size(map);
return map;
}
NcHashmap *nc_hashmap_new(nc_hash_create_func create, nc_hash_compare_func compare)
{
return nc_hashmap_new_internal(create, compare, NULL, NULL);
}
NcHashmap *nc_hashmap_new_full(nc_hash_create_func create, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free)
{
return nc_hashmap_new_internal(create, compare, key_free, value_free);
}
static inline unsigned nc_hashmap_get_hash(NcHashmap *self, const void *key)
{
unsigned hash = self->hash(key);
return hash;
}
static bool nc_hashmap_insert_bucket(NcHashmap *self, NcHashmapEntry *buckets, int n_buckets,
unsigned hash, const void *key, void *value)
{
NcHashmapEntry *row = &(buckets[hash % n_buckets]);
NcHashmapEntry *head = NULL;
NcHashmapEntry *parent = head = row;
bool can_replace = false;
NcHashmapEntry *tomb = NULL;
int ret = 1;
while (row) {
if (!row->occ) {
tomb = row;
}
parent = row;
if (row->occ && row->hash == key) {
if (self->compare(row->hash, key)) {
can_replace = true;
break;
}
}
row = row->next;
}
if (can_replace) {
/* Replace existing allocations. */
if (self->value_free) {
self->value_free(row->value);
}
if (self->key_free) {
self->key_free(row->hash);
}
ret = 0;
} else if (tomb) {
row = tomb;
}
if (!row) {
row = calloc(1, sizeof(NcHashmapEntry));
if (!row) {
return -1;
}
}
row->hash = (void *)key;
row->value = value;
row->occ = true;
if (parent != row && parent) {
parent->next = row;
}
return ret;
}
bool nc_hashmap_put(NcHashmap *self, const void *key, void *value)
{
if (!self) {
return false;
}
int inc;
if (nc_hashmap_maybe_resize(self)) {
if (!nc_hashmap_resize(self)) {
return false;
}
}
unsigned hash = nc_hashmap_get_hash(self, key);
inc = nc_hashmap_insert_bucket(self, self->buckets, self->n_buckets, hash, key, value);
if (inc > 0) {
self->size += inc;
return true;
} else {
return false;
}
}
static NcHashmapEntry *nc_hashmap_get_entry(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
unsigned hash = nc_hashmap_get_hash(self, key);
NcHashmapEntry *row = &(self->buckets[hash % self->n_buckets]);
while (row) {
if (self->compare(row->hash, key)) {
return row;
}
row = row->next;
}
return NULL;
}
void *nc_hashmap_get(NcHashmap *self, const void *key)
{
if (!self) {
return NULL;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (row) {
return row->value;
}
return NULL;
}
bool nc_hashmap_ensure_get(NcHashmap *self, const void *key, void **value)
{
if (!self) {
return false;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (!row) {
*value = NULL;
return false;
}
*value = row->value;
return true;
}
static bool nc_hashmap_remove_internal(NcHashmap *self, const void *key, bool remove)
{
if (!self) {
return false;
}
NcHashmapEntry *row = nc_hashmap_get_entry(self, key);
if (!row) {
return false;
}
if (remove) {
if (self->key_free) {
self->key_free(row->hash);
}
if (self->value_free) {
self->value_free(row->value);
}
}
self->size -= 1;
row->hash = NULL;
row->value = NULL;
row->occ = false;
return true;
}
bool nc_hashmap_steal(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, false);
}
bool nc_hashmap_remove(NcHashmap *self, const void *key)
{
return nc_hashmap_remove_internal(self, key, true);
}
bool nc_hashmap_contains(NcHashmap *self, const void *key)
{
return (nc_hashmap_get(self, key)) != NULL;
}
static inline void nc_hashmap_free_bucket(NcHashmap *self, NcHashmapEntry *bucket, bool nuke)
{
if (!self) {
return;
}
NcHashmapEntry *tmp = bucket;
NcHashmapEntry *bk = bucket;
NcHashmapEntry *root = bucket;
while (tmp) {
bk = NULL;
if (tmp->next) {
bk = tmp->next;
}
if (nuke && tmp->occ) {
if (self->key_free) {
self->key_free(tmp->hash);
}
if (self->value_free) {
self->value_free(tmp->value);
}
}
if (tmp != root) {
free(tmp);
}
tmp = bk;
}
}
void nc_hashmap_free(NcHashmap *self)
{
if (!self) {
return;
}
for (int i = 0; i < self->n_buckets; i++) {
NcHashmapEntry *row = &(self->buckets[i]);
nc_hashmap_free_bucket(self, row, true);
}
if (self->buckets) {
free(self->buckets);
}
free(self);
}
static void nc_hashmap_update_next_size(NcHashmap *self)
{
if (!self) {
return;
}
self->next_size = (int)(self->n_buckets * FULL_FACTOR);
}
int nc_hashmap_size(NcHashmap *self)
{
if (!self) {
return -1;
}
return self->size;
}
static bool nc_hashmap_resize(NcHashmap *self)
{
if (!self || !self->buckets) {
return false;
}
NcHashmapEntry *old_buckets = self->buckets;
NcHashmapEntry *new_buckets = NULL;
NcHashmapEntry *entry = NULL;
int incr;
int old_size, new_size;
int items = 0;
new_size = old_size = self->n_buckets;
new_size *= INCREASE_FACTOR;
new_buckets = calloc(new_size, sizeof(NcHashmapEntry));
if (!new_buckets) {
return false;
}
for (int i = 0; i < old_size; i++) {
entry = &(old_buckets[i]);
while (entry) {
if (entry->occ) {
unsigned hash = nc_hashmap_get_hash(self, entry->hash);
if ((incr = nc_hashmap_insert_bucket(self,
new_buckets,
new_size,
hash,
entry->hash,
entry->value)) > 0) {
items += incr;
} else {
/* Likely a memory issue */
goto failure;
}
}
entry = entry->next;
}
}
/* Successfully resized - do this separately because we need to
* gaurantee old data is preserved */
for (int i = 0; i < old_size; i++) {
nc_hashmap_free_bucket(self, &(old_buckets[i]), false);
}
free(old_buckets);
self->n_buckets = new_size;
self->size = items;
self->buckets = new_buckets;
nc_hashmap_update_next_size(self);
return true;
failure:
for (int i = 0; i < new_size; i++) {
nc_hashmap_free_bucket(self, &(new_buckets[i]), true);
}
free(new_buckets);
return false;
}
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *citer)
{
_NcHashmapIter *iter = NULL;
if (!map || !citer) {
return;
}
iter = (_NcHashmapIter *)citer;
_NcHashmapIter it = {
.bucket = -1, .map = map, .item = NULL,
};
*iter = it;
}
bool nc_hashmap_iter_next(NcHashmapIter *citer, void **key, void **value)
{
_NcHashmapIter *iter = NULL;
NcHashmapEntry *item = NULL;
NcHashmap *map = NULL;
int n_buckets = 0;
if (!citer) {
return false;
}
iter = (_NcHashmapIter *)citer;
map = iter->map;
if (!map) {
return false;
}
n_buckets = map->n_buckets;
item = iter->item;
for (;;) {
if (iter->bucket >= n_buckets) {
if (item && !item->next) {
return false;
}
}
if (!item) {
iter->bucket++;
if (iter->bucket > n_buckets - 1) {
return false;
}
item = &(map->buckets[iter->bucket]);
}
if (item && item->occ) {
goto success;
}
item = item->next;
}
return false;
success:
iter->item = item->next;
if (key) {
*key = item->hash;
}
if (value) {
*value = item->value;
}
return true;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+254
View File
@@ -0,0 +1,254 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This is a chained Hashmap implementation. It focuses on being clean
* and efficient, and is comparable (at -O2) to open addressing hashmaps
* up until around 105,000 elements, where open addressing will begin
* to come out in front. At 1 million elements, open addressing is a clear
* winner, however currently we only need a maximum of 70k elements in
* our implementation.
*
* Given the memory required to even begin to deal with 1 million elements,
* it becomes very questionable whether a hashmap should have even been
* used in the first place (mmap'd db?)
*
* Authors:
*
* Ikey Doherty <michael.i.doherty@intel.com>
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdint.h>
#include "util.h"
/* Convert between uint and void* */
#define NC_HASH_KEY(x) ((void *)((uintptr_t)(x)))
#define NC_HASH_VALUE(x) NC_HASH_KEY(x)
#define NC_UNHASH_KEY(x) ((unsigned int)((uintptr_t)(x)))
#define NC_UNHASH_VALUE(x) NC_UNHASH_KEY(x)
typedef struct NcHashmap NcHashmap;
/**
* Iteration object
*/
typedef struct NcHashmapIter {
int n0;
void *n1;
void *n2;
} NcHashmapIter;
/**
* Hash comparison function definition
*
* @param l First value to compare
* @param r Second value to compare
*
* @return true if l and r both match, otherwise false
*/
typedef bool (*nc_hash_compare_func)(const void *l, const void *r);
/**
* Hash creation function definition
*
* @param key Key to generate a hash for
*
* @return an unsigned integer hash result
*/
typedef unsigned (*nc_hash_create_func)(const void *key);
/**
* Callback definition to free keys and values
*
* @param p Single-depth pointer to either a key or value that should be freed
*/
typedef void (*nc_hash_free_func)(void *p);
/**
* Default hash/comparison functions
*
* @note These are only used for comparison of *keys*, not values. Unless
* explicitly using string keys, you should most likely stick with the default
* nc_simple_hash and nc_simple_compare functions
*/
/* Default string hash */
static inline unsigned nc_string_hash(const void *key)
{
unsigned hash = 5381;
const signed char *c;
/* DJB's hash function */
for (c = key; *c != '\0'; c++) {
hash = (hash << 5) + hash + (unsigned)*c;
}
return hash;
}
/**
* Trivial pointer->uint hash
*/
static inline unsigned nc_simple_hash(const void *source)
{
return NC_UNHASH_KEY(source);
}
/**
* Comparison of string keys
*/
static inline bool nc_string_compare(const void *l, const void *r)
{
if (!l || !r) {
return false;
}
return (strcmp(l, r) == 0);
}
/**
* Trivial pointer comparison
*/
static inline bool nc_simple_compare(const void *l, const void *r)
{
return (l == r);
}
/**
* Create a new NcHashmap
*
* @param hash Hash creation function
* @param compare Key comparison function
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new(nc_hash_create_func hash, nc_hash_compare_func compare);
/**
* Create a new NcHashmap with cleanup functions
*
* @param hash Hash creation function
* @param compare Key comparison function
* @param key_free Function to free keys when removed/destroyed
* @param value_free Function to free values when removed/destroyed
*
* @return A newly allocated NcHashmap
*/
NcHashmap *nc_hashmap_new_full(nc_hash_create_func hash, nc_hash_compare_func compare,
nc_hash_free_func key_free, nc_hash_free_func value_free);
/**
* Store a key/value pair in the hashmap
*
* @note This will displace duplicate keys, and may free both the key
* and value if key_free and value_free are non null
*
* @param key Key to store in the hashmap
* @param value Value to be associated with the key
*
* @return true if the operation succeeded.
*/
bool nc_hashmap_put(NcHashmap *map, const void *key, void *value);
/**
* Get the value associated with the unique key
*
* @param key Unique key to obtain a value for
* @return The associated value if it exists, otherwise NULL
*/
void *nc_hashmap_get(NcHashmap *map, const void *key);
/**
* Ensure the value is definitely contained in the map and set
* the resulting pointer with it's value.
*
* @param key Unique key to obtain a value for
* @param value The associated value if it exists, otherwise NULL
*
* @returns True if the key exists, otherwise false
*/
bool nc_hashmap_ensure_get(NcHashmap *map, const void *key, void **value);
/**
* Determine if the key has an associated value in the NcHashmap
*
* @param key Unique key to check value for
* @return True if the key exists in the hashmap
*/
bool nc_hashmap_contains(NcHashmap *self, const void *key);
/**
* Free the given NcHashmap, and all keys/values if appropriate
*/
void nc_hashmap_free(NcHashmap *map);
/**
* Remove the value and key identified by key.
*
* @note If key_free or value_free are non-NULL, they will be invoked
* for both the key and value being removed
*
* @param key The unique key to remove
* @return true if the key/value pair were removed
*/
bool nc_hashmap_remove(NcHashmap *map, const void *key);
/**
* Remove the value and key identified by key without freeing them
*
* @return true if the key/value pair were stolen
*/
bool nc_hashmap_steal(NcHashmap *map, const void *key);
/**
* Return the current size of the hashmap
*
* @return size of the current hashmap (element count)
*/
int nc_hashmap_size(NcHashmap *map);
/**
* Initialise a NcHashmapIter for iteration purposes
*
* @note The iter *must* be re-inited to re-use, or if it becomes
* exhausted from a previous iteration run
*
* @param iter pointer to a NcHashmapIter
*/
void nc_hashmap_iter_init(NcHashmap *map, NcHashmapIter *iter);
/**
* Iterate every key/value pair in the hashmap
*
* @param iter A correctly initialised NcHashmapIter
* @param key Pointers to store the key in, may be NULL to skip
* @param value Pointer to store the value in, may be NULL to skip
*
* @return true if it's possible to iterate
*/
bool nc_hashmap_iter_next(NcHashmapIter *iter, void **key, void **value);
DEF_AUTOFREE(NcHashmap, nc_hashmap_free)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+58
View File
@@ -0,0 +1,58 @@
/*
* This file is part of libnica.
*
* Copyright (C) 2016 Intel Corporation
*
* libnica is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Authors:
*
* Ikey Doherty <michael.i.doherty@intel.com>
*/
#pragma once
#define _GNU_SOURCE
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEF_AUTOFREE(N, C) \
static inline void _autofree_func_##N(void *p) \
{ \
if (p && *(N **)p) { \
C(*(N **)p); \
(*(void **)p) = NULL; \
} \
}
#define autofree(N) __attribute__((cleanup(_autofree_func_##N))) N
#define streq(x, y) strcmp(x, y) == 0 ? true : false
#define __nc_unused__ __attribute__((unused))
#define __nc_inline__ __attribute__((always_inline))
#define ARRAY_SIZE(x) sizeof(x) / sizeof(x[0])
DEF_AUTOFREE(char, free)
DEF_AUTOFREE(FILE, fclose)
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 8
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=8 tabstop=8 expandtab:
* :indentSize=8:tabSize=8:noTabs=true:
*/
+160 -134
View File
@@ -20,25 +20,26 @@
* *
* Authors: * Authors:
* Arjan van de Ven <arjanvandeven@gmail.com> * Arjan van de Ven <arjanvandeven@gmail.com>
* Ikey Doherty <michael.i.doherty@intel.com>
* *
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include <dirent.h>
#include <errno.h>
#include <libgen.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/socket.h>
#include <dirent.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <libgen.h> #include <sys/types.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <glib.h> #include "nica/files.h"
/* /*
@@ -53,165 +54,190 @@ as much as possible.
static void unsymlink(char *filename) static void unsymlink(char *filename)
{ {
char *target; char *target;
__nc_unused__ int r;
target = canonicalize_file_name(filename); target = canonicalize_file_name(filename);
// sleep(1); // sleep(1);
// printf("Symlink %s points to %s\n", filename, target); // printf("Symlink %s points to %s\n", filename, target);
unlink(filename); unlink(filename);
link(target, filename); r = link(target, filename);
} }
static void wait_for_loadavg(void) static void wait_for_loadavg(void)
{ {
FILE *file; FILE *file;
char line[4096]; char line[4096];
while (1) { __nc_unused__ char *ret = NULL;
double d;
file = fopen("/proc/loadavg", "r");
if (!file)
return;
line[0] = 0;
fgets(line, 4096, file);
fclose(file);
d = strtod(line, NULL);
if (d < 50)
return;
sleep(5);
} while (1) {
double d;
file = fopen("/proc/loadavg", "r");
if (!file) {
return;
}
line[0] = 0;
ret = fgets(line, 4096, file);
fclose(file);
d = strtod(line, NULL);
if (d < 50) {
return;
}
sleep(5);
}
} }
int tarcount; int tarcount;
static void do_one_file(char *base1, char *base2, char *path, int isdir) static void do_one_file(char *base1, char *base2, char *path, int isdir)
{ {
char *fullpath1 = NULL, *fullpath2 = NULL, *dir, *dir2; char *dir2 = NULL;
struct stat buf1, buf2; autofree(char) * fullpath1, *fullpath2, *dir = NULL;
int ret; struct stat buf1, buf2;
int ret;
int sret;
if (asprintf(&fullpath1, "%s%s", base1, path) < 0) if (asprintf(&fullpath1, "%s%s", base1, path) < 0) {
return; return;
if (asprintf(&fullpath2, "%s%s.tar", base2, path) < 0) { }
free(fullpath1); if (asprintf(&fullpath2, "%s%s.tar", base2, path) < 0) {
return; return;
} }
ret = lstat(fullpath1, &buf1); ret = lstat(fullpath1, &buf1);
if (ret) if (ret) {
goto out; return;
}
if (S_ISLNK(buf1.st_mode)) { if (S_ISLNK(buf1.st_mode)) {
// printf("%s is a symlink\n", fullpath1); // printf("%s is a symlink\n", fullpath1);
ret = stat(fullpath1, &buf2); ret = stat(fullpath1, &buf2);
if (!ret) if (!ret) {
unsymlink(fullpath1); unsymlink(fullpath1);
ret = lstat(fullpath1, &buf1); }
} ret = lstat(fullpath1, &buf1);
}
ret = lstat(fullpath2, &buf2); ret = lstat(fullpath2, &buf2);
if (ret || buf1.st_mtime > buf2.st_mtime) { if (ret || buf1.st_mtime > buf2.st_mtime) {
char *command = NULL; char *command = NULL;
unlink(fullpath2); unlink(fullpath2);
dir = strdup(fullpath2); dir = strdup(fullpath2);
dir2 = dirname(dir); dir2 = dirname(dir);
if (asprintf(&command, "mkdir -p %s", dir2) >= 0) { if (dir2) {
// printf("Running -%s-\n", command); (void)nc_mkdir_p(dir2, 00755);
system(command); }
free(command);
}
free(dir); if (chdir(base1) != 0) {
chdir(base1); fprintf(stderr, "Failed to chdir: %s\n", strerror(errno));
if (!isdir && asprintf(&command, "tar --no-recursion -C %s -Jcf %s %s &", base1, fullpath2, path) >= 0) { return;
// printf("Running -%s-\n", command); }
printf("Processing %s\n", fullpath2);
tarcount++;
if (tarcount > 50) {
wait_for_loadavg();
tarcount = 0;
}
system(command);
free(command);
}
if (isdir && asprintf(&command, "tar --no-recursion -C %s -Jcf %s `find %s -type d` `find %s -maxdepth 1 -type l` &", base1, fullpath2, path, path) >= 0) { if (!isdir &&
// printf("Running -%s-\n", command); asprintf(&command,
printf("Processing %s\n", fullpath2); "tar --no-recursion -C %s -Jcf %s %s &",
tarcount++; base1,
if (tarcount > 50) { fullpath2,
wait_for_loadavg(); path) >= 0) {
tarcount = 0; // printf("Running -%s-\n", command);
} printf("Processing %s\n", fullpath2);
system(command); tarcount++;
free(command); if (tarcount > 50) {
} wait_for_loadavg();
tarcount = 0;
}
if ((sret = system(command)) != 0) {
fprintf(stderr, "Failure in command: [%d] %s\n", sret, command);
}
free(command);
}
} if (isdir &&
asprintf(&command,
out: "tar --no-recursion -C %s -Jcf %s `find %s -type d` `find %s "
free(fullpath1); "-maxdepth 1 -type l` &",
free(fullpath2); base1,
fullpath2,
path,
path) >= 0) {
// printf("Running -%s-\n", command);
printf("Processing %s\n", fullpath2);
tarcount++;
if (tarcount > 50) {
wait_for_loadavg();
tarcount = 0;
}
if ((sret = system(command)) != 0) {
fprintf(stderr, "Failure in command: [%d] %s\n", sret, command);
}
free(command);
}
}
} }
static void recurse_dir(char *base1, char *base2, char *path) static void recurse_dir(char *base1, char *base2, char *path)
{ {
DIR *dir; DIR *dir;
char *fullpath1, *fullpath2, *newpath; char *fullpath1 = NULL;
struct dirent *entry; struct dirent *entry;
fullpath1 = NULL; if (asprintf(&fullpath1, "%s%s", base1, path) < 0) {
fullpath2 = NULL; return;
}
dir = opendir(fullpath1);
if (asprintf(&fullpath1, "%s%s", base1, path) < 0) if (!dir) {
return; return;
}
dir = opendir(fullpath1); while (1) {
autofree(char) *fullpath2 = NULL;
autofree(char) *newpath = NULL;
if (!dir) { entry = readdir(dir);
free(fullpath1); if (!entry) {
return; break;
} }
while (1) { if (strcmp(entry->d_name, ".") == 0) {
entry = readdir(dir); continue;
if (!entry) }
break; if (strcmp(entry->d_name, "..") == 0) {
if (strcmp(entry->d_name, ".") == 0) continue;
continue; }
if (strcmp(entry->d_name, "..") == 0)
continue;
newpath = NULL; newpath = NULL;
fullpath2 = NULL; fullpath2 = NULL;
asprintf(&fullpath2, "%s/%s", fullpath1, entry->d_name); if (asprintf(&fullpath2, "%s/%s", fullpath1, entry->d_name) < 0) {
if (asprintf(&newpath, "%s/%s", path, entry->d_name) >= 0) { return;
struct stat sb; }
stat(fullpath2, &sb); if (asprintf(&newpath, "%s/%s", path, entry->d_name) >= 0) {
struct stat sb;
stat(fullpath2, &sb);
if (entry->d_type == DT_DIR || S_ISDIR(sb.st_mode)) { if (entry->d_type == DT_DIR || S_ISDIR(sb.st_mode)) {
do_one_file(base1, base2, newpath, 1); do_one_file(base1, base2, newpath, 1);
recurse_dir(base1, base2, newpath); recurse_dir(base1, base2, newpath);
} else { } else {
do_one_file(base1, base2, newpath, 0); do_one_file(base1, base2, newpath, 0);
} }
free(newpath); }
} }
} closedir(dir);
closedir(dir);
free(fullpath1);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc < 0) if (argc < 0)
(void)argv; (void)argv;
recurse_dir("/var/www/html/debuginfo.raw/usr/lib/debug/", "/var/www/html/debuginfo/lib/", "."); recurse_dir("/var/www/html/debuginfo.raw/usr/lib/debug/",
recurse_dir("/var/www/html/debuginfo.raw/usr/src/debug/", "/var/www/html/debuginfo/src/", "."); "/var/www/html/debuginfo/lib/",
".");
recurse_dir("/var/www/html/debuginfo.raw/usr/src/debug/",
"/var/www/html/debuginfo/src/",
".");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+271 -213
View File
@@ -20,292 +20,350 @@
* *
* Authors: * Authors:
* Arjan van de Ven <arjanvandeven@gmail.com> * Arjan van de Ven <arjanvandeven@gmail.com>
* Ikey Doherty <michael.i.doherty@intel.com>
* *
*/ */
#define _GNU_SOURCE #define _GNU_SOURCE
#include <errno.h>
#include <malloc.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h> #include <string.h>
#include <pthread.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <malloc.h> #include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include "nica/files.h"
#include "nica/hashmap.h"
#include <curl/curl.h> #include <curl/curl.h>
#include <glib.h>
static GMutex dupes_mutex; static pthread_mutex_t dupes_mutex = PTHREAD_MUTEX_INITIALIZER;
char *urls[2] = {"https://debuginfo.clearlinux.org/debuginfo/", "http://debuginfo.fenrus.org/debuginfo/" }; char *urls[2] = { "https://debuginfo.clearlinux.org/debuginfo/",
"https://debuginfo.clearlinux.org/debuginfo/" };
int urlcounter = 1; int urlcounter = 1;
static NcHashmap *hash = NULL;
static GHashTable *hash; #define MAX_CONNECTIONS 16
static atomic_int current_connection_count = 0;
static int avoid_dupes(const char *url) static int avoid_dupes(const char *url)
{ {
int retval = 0; int retval = 0;
void *value; void *value;
g_mutex_lock(&dupes_mutex); pthread_mutex_lock(&dupes_mutex);
if (hash == NULL) if (hash == NULL) {
hash = g_hash_table_new(g_str_hash, g_str_equal); hash = nc_hashmap_new_full(nc_string_hash, nc_string_compare, free, NULL);
}
if (g_hash_table_lookup_extended(hash, url, NULL, &value)) { if (nc_hashmap_ensure_get(hash, url, &value)) {
unsigned long tm; unsigned long tm;
tm = (unsigned long)value; tm = (unsigned long)value;
if (time(NULL) - tm < 600) { if (time(NULL) - tm < 600) {
retval = 1; retval = 1;
} }
} else { } else {
unsigned long tm; unsigned long tm;
tm = time(NULL); tm = time(NULL);
void *data = (void *)(unsigned long)tm; void *data = (void *)(unsigned long)tm;
g_hash_table_replace(hash, strdup(url), data); nc_hashmap_put(hash, strdup(url), data);
} }
g_mutex_unlock(&dupes_mutex); pthread_mutex_unlock(&dupes_mutex);
return retval; return retval;
}
/**
* Get the current connection count atomically
*/
__nc_inline__ static inline int get_current_connection_count(void)
{
return atomic_load(&current_connection_count);
}
/**
* Increment the connection counter atomically
*/
__nc_inline__ static inline void inc_connection_count(void)
{
atomic_fetch_add(&current_connection_count, 1);
}
/**
* Decrement the connection counter atomically
*/
__nc_inline__ static inline void dec_connection_count(void)
{
atomic_fetch_sub(&current_connection_count, 1);
} }
static int curl_get_file(const char *url, const char *prefix, time_t timestamp) static int curl_get_file(const char *url, const char *prefix, time_t timestamp)
{ {
CURLcode code; CURLcode code;
long ret; long ret;
int fd; int fd;
char filename[PATH_MAX]; char filename[PATH_MAX];
CURL *curl = NULL; CURL *curl = NULL;
FILE *file; FILE *file;
struct stat statbuf; struct stat statbuf;
if (avoid_dupes(url)) if (avoid_dupes(url)) {
return 300; return 300;
}
curl = curl_easy_init();
if (curl == NULL) {
return 301;
}
curl = curl_easy_init(); strcpy(filename, "/tmp/clr-debug-info-XXXXXX");
if (curl == NULL)
return 301;
fd = mkstemp(filename);
if (fd < 0) {
curl_easy_cleanup(curl);
return 500;
}
strcpy(filename, "/tmp/clr-debug-info-XXXXXX"); file = fdopen(fd, "w");
fd = mkstemp(filename); curl_easy_setopt(curl, CURLOPT_URL, url);
if (fd < 0) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
curl_easy_cleanup(curl);
return 500;
}
file = fdopen(fd, "w"); if (timestamp) {
curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
curl_easy_setopt(curl, CURLOPT_TIMEVALUE, timestamp);
}
curl_easy_setopt(curl, CURLOPT_URL, url); code = curl_easy_perform(curl);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
if (timestamp) { /* can't trust the file if we get an error back */
curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); if (code != 0) {
curl_easy_setopt(curl, CURLOPT_TIMEVALUE, timestamp); unlink(filename);
} }
code = curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
fflush(file);
// printf("HTTP return code is %i\n", ret);
/* can't trust the file if we get an error back */ /* HTTP 304 is returned if (a) the cached debuginfo has the same
if (code != 0) * timestamp or is newer than that on the server and (b) we haven't
unlink(filename); * already added the URL to the hash table. So, the first crash for a
* boot may result in a 304 if the debuginfo had been downloaded in a
* previous boot.
*/
if (ret != 200 && ret != 404 && ret != 304) {
urlcounter++;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret); if (ret == 200) {
fflush(file); autofree(char) *command = NULL;
// printf("HTTP return code is %i\n", ret); // printf("Filename is %s\n", filename);
/* HTTP 304 is returned if (a) the cached debuginfo has the same memset(&statbuf, 0, sizeof(statbuf));
* timestamp or is newer than that on the server and (b) we haven't stat(filename, &statbuf);
* already added the URL to the hash table. So, the first crash for a if (statbuf.st_size > 0 &&
* boot may result in a 304 if the debuginfo had been downloaded in a asprintf(&command,
* previous boot. "tar -C /var/cache/debuginfo/%s --no-same-owner -xf %s",
*/ prefix,
if (ret != 200 && ret != 404 && ret != 304) filename) >= 0) {
urlcounter++; if (system(command) != 0) {
fputs("Warning: tar extraction failed\n", stderr);
if (ret == 200) { }
char *command = NULL; }
// printf("Filename is %s\n", filename); }
unlink(filename);
memset(&statbuf, 0, sizeof(statbuf)); curl_easy_cleanup(curl);
stat(filename, &statbuf); fclose(file);
if (statbuf.st_size > 0 && asprintf(&command, "tar -C /var/cache/debuginfo/%s --no-same-owner -xf %s", prefix, filename) >= 0) { return ret;
system(command);
free(command);
}
}
unlink(filename);
curl_easy_cleanup(curl);
fclose(file);
return ret;
} }
double timedelta(struct timeval before, struct timeval after) double timedelta(struct timeval before, struct timeval after)
{ {
double d; double d;
d = 1.0 * (after.tv_sec - before.tv_sec) + (1.0 * after.tv_usec - before.tv_usec)/1000000.0; d = 1.0 * (after.tv_sec - before.tv_sec) +
return d; (1.0 * after.tv_usec - before.tv_usec) / 1000000.0;
return d;
} }
static void *server_thread(void *arg) static void *server_thread(void *arg)
{ {
int fd; int fd = -1;
char buf[PATH_MAX+8]; char buf[PATH_MAX + 8];
int ret; int ret;
char *prefix, *path, *url, *c; char *prefix, *path, *c = NULL;
time_t timestamp; autofree(char) *url = NULL;
struct timeval before, after; time_t timestamp;
struct timeval before, after;
__nc_unused__ size_t wr = -1;
fd = (unsigned long)arg; fd = (unsigned long)arg;
if (fd < 0) if (fd < 0) {
return NULL; goto thread_end;
}
gettimeofday(&before, NULL); gettimeofday(&before, NULL);
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, PATH_MAX + 8);
if (ret < 0) {
goto thread_end;
}
prefix = buf;
c = strchr(buf, ':');
if (!c) {
goto thread_end;
}
*c = 0;
timestamp = strtoull(buf, NULL, 10);
c++;
prefix = c;
path = strchr(c, ':');
if (!path) {
goto thread_end;
}
*path = 0;
path++;
memset(buf, 0, sizeof(buf)); /* GDB and elfutils both stat /usr/lib/debug directly when looking up
ret = read(fd, buf, PATH_MAX+8); * debuginfo, so avoid the download for "/.tar"; the associated cache
if (ret < 0) { * directories already exist by this point.
close(fd); */
return NULL; if (strlen(path) == 1 && strcmp(path, "/") == 0) {
} goto thread_end;
prefix = buf; }
c = strchr(buf, ':');
if (!c) {
close(fd);
return NULL;
}
*c = 0;
timestamp = strtoull(buf, NULL, 10);
c++;
prefix = c;
path = strchr(c, ':');
if (!path) {
close(fd);
return NULL;
}
*path = 0;
path++;
/* GDB and elfutils both stat /usr/lib/debug directly when looking up if (strstr(path, "..") || strstr(prefix, "..") || strstr(path, "'") || strstr(path, ";")) {
* debuginfo, so avoid the download for "/.tar"; the associated cache goto thread_end;
* directories already exist by this point. }
*/
if (strlen(path) == 1 && strcmp(path, "/") == 0) {
close(fd);
return NULL;
}
if (strstr(path, "..") || strstr(prefix, "..") || strstr(path, "'") || strstr(path, ";")) { if (strcmp(prefix, "lib") != 0 && strcmp(prefix, "src") != 0) {
close(fd); /* invalid prefix */
return NULL; goto thread_end;
} }
url = NULL;
if (asprintf(&url, "%s%s%s.tar", urls[urlcounter % 2], prefix, path) < 0) {
goto thread_end;
}
if (strcmp(prefix, "lib") !=0 && strcmp(prefix, "src") != 0) { // printf("Getting url %s %i:%06i\n", url, before.tv_sec, before.tv_usec);
/* invalid prefix */ ret = curl_get_file(url, prefix, timestamp);
close(fd);
return NULL;
}
url = NULL;
if (asprintf(&url, "%s%s%s.tar", urls[urlcounter % 2], prefix, path) < 0) {
close(fd);
return NULL;
}
// printf("Getting url %s %i:%06i\n", url, before.tv_sec, before.tv_usec); switch (ret) {
ret = curl_get_file(url, prefix, timestamp); case 200:
case 300:
case 304:
case 404:
// ignore these error codes
break;
default:
printf("Request for %s resulted in error %i\n", url, ret);
break;
}
switch (ret) { gettimeofday(&after, NULL);
case 200: if (timedelta(before, after) > 0.6)
case 300: printf("Request for %s took %5.2f seconds (%i - %i)\n",
case 304: url,
case 404: after.tv_sec - before.tv_sec +
// ignore these error codes (1.0 * after.tv_usec - before.tv_usec) / 1000000.0,
break; ret,
default: (int)timestamp);
printf("Request for %s resulted in error %i\n", url, ret);
break;
}
gettimeofday(&after, NULL); /* tell the other side we're done with the download */
if (timedelta(before, after) > 0.6) wr = write(fd, "ok", 3);
printf("Request for %s took %5.2f seconds (%i - %i)\n", url,
after.tv_sec - before.tv_sec + (1.0*after.tv_usec - before.tv_usec) / 1000000.0, ret, (int)timestamp);
/* tell the other side we're done with the download */ thread_end:
write(fd, "ok", 3); if (fd >= 0) {
close(fd); close(fd);
}
dec_connection_count();
free(url); return NULL;
return NULL;
} }
int main(__nc_unused__ int argc, __nc_unused__ char **argv)
int main(int argc, char **argv)
{ {
int sockfd; int sockfd;
struct sockaddr_un sun; struct sockaddr_un sun;
int ret; int ret;
int curl_done = 0; int curl_done = 0;
const char *required_paths[] = { "/var/cache/debuginfo/lib", "/var/cache/debuginfo/src" };
if (argc < -1) for (size_t i = 0; i < ARRAY_SIZE(required_paths); i++) {
(void)argv; const char *req_path = required_paths[i];
if (nc_file_exists(req_path)) {
continue;
}
if (!nc_mkdir_p(req_path, 00755)) {
fprintf(stderr, "Failed to mkdir: %s %s\n", strerror(errno), req_path);
return EXIT_FAILURE;
}
}
signal(SIGPIPE, SIG_IGN);
signal(SIGPIPE,SIG_IGN); sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
return EXIT_FAILURE;
}
if (access("/var/cache/debuginfo/lib/", F_OK)) sun.sun_family = AF_UNIX;
system("mkdir -p /var/cache/debuginfo/lib/ &> /dev/null"); strcpy(sun.sun_path, ":clr-debug-info");
if (access("/var/cache/debuginfo/src/", F_OK)) sun.sun_path[0] = 0; /* anonymous unix socket */
system("mkdir -p /var/cache/debuginfo/src/ &> /dev/null");
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); ret = bind(sockfd,
if (sockfd < 0) (struct sockaddr *)&sun,
return EXIT_FAILURE; offsetof(struct sockaddr_un, sun_path) + strlen(":clr-debug-info") + 1);
if (ret < 0) {
printf("Failed to bind:%s \n", strerror(errno));
return EXIT_FAILURE;
}
sun.sun_family = AF_UNIX; if (listen(sockfd, 16) < 0) {
strcpy(sun.sun_path, ":clr-debug-info"); printf("Failed to listen:%s \n", strerror(errno));
sun.sun_path[0] = 0; /* anonymous unix socket */ return EXIT_FAILURE;
}
ret = bind(sockfd, (struct sockaddr *)&sun, offsetof(struct sockaddr_un, sun_path) + strlen(":clr-debug-info") + 1); while (1) {
if (ret < 0) { int clientsock;
printf("Failed to bind:%s \n", strerror(errno)); pthread_t thread;
return EXIT_FAILURE;
}
if (listen(sockfd, 16) < 0) { malloc_trim(0);
printf("Failed to listen:%s \n", strerror(errno)); clientsock = accept(sockfd, NULL, NULL);
return EXIT_FAILURE;
}
/* Too many connections, wait for the next loop/retry */
if (get_current_connection_count() >= MAX_CONNECTIONS) {
/* printf("Too many connections!\n"); */
shutdown(clientsock, SHUT_RDWR);
close(clientsock);
continue;
}
while (1) { if (!curl_done) {
int clientsock; curl_global_init(CURL_GLOBAL_ALL);
pthread_t thread; curl_done = 1;
}
malloc_trim(0); /* Update the connection count and start the new thread */
clientsock = accept(sockfd, NULL, NULL); inc_connection_count();
pthread_create(&thread, NULL, server_thread, (void *)(unsigned long)clientsock);
pthread_detach(thread);
}
if (!curl_done) { if (hash) {
curl_global_init(CURL_GLOBAL_ALL); nc_hashmap_free(hash);
curl_done = 1; }
}
pthread_create(&thread, NULL, server_thread, (void *)(unsigned long)clientsock);
pthread_detach(thread);
}
close(sockfd);
close(sockfd);
} }
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
clang-format -i $(find . -name '*.[ch]')