Compare commits

..
87 Commits
17 .. 51
Author SHA1 Message Date
Patrick McCarty 7cd99f98ed Release v51
Switches the debuginfo tarball compression format from xz to zstd.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-11-13 14:04:27 -08:00
Josue David Hernandez Gutierrez 0387249d7a change xz for zstd
Fixes #4

Signed-off-by: Josue David Hernandez Gutierrez <josue.d.hernandez.gutierrez@intel.com>
2019-11-13 14:02:55 -08:00
Patrick McCarty bca3bdefda Release v50
- Convert clr_debug_prepare to a bash script, also incorporating a few
  improvements and bug fixes into the rewrite.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-06-05 15:34:59 -07:00
Patrick McCarty 179392c601 Convert clr_debug_prepare to a bash script
For easier parallelization, I opted to replace the C program with a bash
script that uses GNU Parallel, find, and GAWK for the core control flow.

For compatibility, running the shell script without arguments has the
same general behavior as the C program. Some bugs have been fixed as
well, so the end result should be an improvement over the original.

Bugs fixed:

- A race condition in the C program with the "unsymlink" operation has
  been addressed: instead of replacing the valid symlinks within
  debuginfo.raw, the symlink targets are added directly to the automatic
  tarballs, and tar's "transform" feature is used to rewrite the filename.

- Running system("tar ... &") to create the tarballs often had the side
  effect of bogging down the system, since the number of active tar
  processes had no upper bound. The shell script rewrite uses GNU
  Parallel, which limits (by default) the number of parallel jobs.

- Directory tarballs were unintentionally re-created whenever content
  within the directories were modified, due to the stat() mtime changing.
  With the new script, directory tarball creation only occurs for those
  directories that do not already exist at the destination.

Once the script begins to create tarballs, performance is largely the
same as the C program. The script has a "precompute" phase where it
determines the list of needed tarball creations up-front, which is
different (and probably a bit slower) than the on-the-fly stat()s used
by the C program, but the runtime cost of this new precompute phase is
negligible compared to the tarball creation phase.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-06-05 15:28:20 -07:00
Patrick McCarty b802948209 Release v49
- List tarball contents prior to extraction to improve robustness
- Plug some memory leaks
- Print errors to stderr instead of stdout

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2019-05-24 13:04:07 -07:00
Auke Kok 4495b923a9 Redo tar extraction - try and make this safe(r).
Instead of extracting to a tmp folder, just tar `tf` it first. This
keeps extracting to the proper locations trivial, at the cost of
decompression the content twice.
2019-05-10 11:08:06 -07:00
Auke Kok 8f7289f028 Test result of tar extraction before trusting the result.
We extract the tarball to a temporary file, before actually allowing
gdb to use this file. This allows us to make sure that gdb doesn't
see the file if it is corrupt. Only if tar exceeds, we rename() the
temp result into the actual needed file. If tar fails, we throw away
the file.
2019-05-07 16:19:36 -07:00
Auke Kok dc376d5e8b Update to v48 2019-01-15 12:47:32 -08:00
Auke Kok c831ecb902 Use select() to time out, and exit.
With socket activation, we can now select() and time out before accept()
and exit, releasing all resource we used back to the system.

The default timeout is set for 10 minutes. If no more connections
arrive, the daemon exits.

Socket activation is done through the clr_debug_daemon.socket unit, which
the clr_debug_fuse service requires, so this should be extra robust when
starting up.

The daemon ignores stat() requests for trash that gnome sends when it
sees the fuse system mounted, and returns the underlying stat() for
the root node instead of a remote value, because there is no value
in fetching this info over curl.
2019-01-09 12:58:42 -08:00
Auke Kok 0978d429b2 Clean up some of the socket activation stuff. 2019-01-08 12:37:56 -08:00
Patrick McCarty 6653992aba Release v47
Fixes a bug with clr_debug_prepare's handling of broken symlinks.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-12-10 17:10:44 -08:00
Patrick McCarty 0af0308dd2 Revert "have tar follow symlinks"
After closer inspection, I've determined that prior to this commit,
clr_debug_prepare was behaving properly with respect to the handling of
symlinks in debuginfo packages. Reverting this commit fixes the issue.

Details:

 - Symlinks under /usr/lib/debug/.build-id that are not broken should be
 unsymlinked so that the actual debuginfo is added to the automatic
 debuginfo tarballs. In the debuginfo packages, these files have a
 .debug extension and point to files under /usr/lib/debug/usr.

 - Symlinks under /usr/lib/debug/.build-id that *are* broken cannot be
 unsymlinked, and therefore, tar should not follow these symlinks,
 because fatal errors (and empty tarballs) are the result. These
 symlinks lack the .debug extension and point to the corresponding
 libraries or binaries under /usr when exposed via clr_debug_fuse. So it
 is correct for the symlinks to be broken when packaged by
 clr_debug_prepare, since they point to non-debuginfo files, and the
 debuginfo.raw directory tree only contains debuginfo content.

This reverts commit 7dcddfd0ca8f5539ef34028a1c680f6e82dcf246.
2018-12-10 17:04:05 -08:00
Patrick McCarty 8ace503438 Release v46
This release fixes a build issue when not passing -Wl,--copy-dt-needed-entries
to the compiler.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-05-25 21:56:43 -07:00
Patrick McCarty 2c87f83adc build: add LIBSYSTEMD_LIBS to link line
For the regular Clear Linux build, the -lsystemd option was not needed at link
time due to -Wl,--copy-dt-needed-entries being used. Without that option
though, the linking of clr_debug_daemon will fail.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-05-25 21:55:28 -07:00
Patrick McCarty 67b9b3acc8 Update gitignore
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-05-25 21:38:06 -07:00
Arjan van de Ven d108c0af64 add cdn-alt 2018-05-17 13:54:16 +00:00
Auke Kok 98e802b4cc Also install socket unit. 2018-04-24 13:04:33 -07:00
Auke Kok 612801b0c8 Make -daemon socket activated. 2018-04-23 15:15:33 -07:00
Arjan van de Ven 3c2ad26baa have tar follow symlinks 2017-12-23 00:49:24 +00:00
Arjan van de Ven d20d585e5c much shorter timeouts 2017-12-10 14:25:07 +00:00
Icarus Sparry 1f3a0f0be4 Extract tar file as dbginfo
Make /var/cache/debuginfo and its contents owned by dgbinfo. Create
the directories as this user, and switch to run as this user.

Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-10-31 13:35:29 -07:00
Icarus Sparry d2d67bdb1d Change tmpfiles.d configuration
Use the dbginfo user for the directories.

Signed-off-by: Icarus Sparry <icarus.w.sparry@intel.com>
2017-10-31 09:47:39 -07:00
Auke Kok 60db7bab0f v43 2017-06-06 13:28:11 -07:00
Auke Kok ef6b11498a DefaultDependencies keyword is only valid in [Unit] context. 2017-06-06 13:27:20 -07:00
Auke Kok efeed3e83f v42. 2017-06-01 16:22:00 -07:00
Auke Kok f5542e7fbb Fetch timestamp from debuginfo server.
We trigger redownloads from the server if the timestamp of
downloaded tar files is too new. An easy way around this issue
is to request the timestamp from the server instead of using
the download time. A `touch` of the tar then assures that
the downloaded file has the same timestamp as the server has.
2017-06-01 16:19:15 -07:00
Arjan van de Ven be8a99da53 allow service to start early 2017-05-07 15:42:30 +00:00
Ikey Doherty c9304402e2 Bump v38 to resync configure + tags
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2017-03-09 17:48:59 +00:00
Ikey Doherty 6e202f13f7 Bump v34
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2017-03-09 17:46:10 +00:00
Ikey Doherty 6735f8f6ed nica/files: Ensure we really do break on a read error
Previously nc_copy_file would return true regardless of a source read
error, flagged in analysis. Ensure we bypass the set of ret to true and
return the correct value in all instances.

Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2017-03-09 15:44:09 +00:00
Ikey Doherty 8b3777ab99 server: Remove useless assignment of prefix
This particular assignment is never used, as if this path fails, we go to
the thread end. We then reassign prefix after we split the input string.

Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2017-03-09 15:38:29 +00:00
Ikey Doherty 84350939ef nica/hashmap: Fix signature issue & item dereference
The signature was incorrect for inserting buckets, as we used an int, not
a boolean. Another issue resolved with this change is the potential
dereferencing of a null pointer by not having checked first if item was
NULL when setting the next pointer.

Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2017-03-09 15:35:29 +00:00
Arjan van de Ven 72b1120a40 add --no-same-permissions as well 2017-03-09 14:52:37 +00:00
Auke Kok d3d5fab301 Tag v33 2016-11-01 09:20:19 -07:00
Auke Kok e377433a9d Return on error here too.
Missed a 'return' catching an error.
2016-11-01 09:15:12 -07:00
Auke Kok afa92c77b1 Add reasonable connection timeout limits.
Adds a 30second connection timeout, and a low bandwith timeout
value (at 1kb/sec over 30 seconds) where the attempt will fail.
This provides some feedback to users that network issues are
preventing debug info from being loaded.
2016-11-01 09:15:12 -07:00
Arjan van de Ven a01ce4c329 dumb down due to limited infra 2016-11-01 13:17:49 +00:00
Arjan van de Ven 1eaaac582c use http/2 2016-11-01 12:56:35 +00:00
Arjan van de Ven 935dbee3c7 use CDN urls 2016-11-01 12:53:04 +00:00
Arjan van de Ven 0065ef2670 don't spew the journal 2016-11-01 12:52:17 +00:00
Ikey Doherty b5b4030f7c Release v32
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-18 19:21:20 +01:00
Ikey Doherty de9e0ce5c5 server: Disallow gdb -p attach, drop CAP_SYS_ADMIN
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-18 15:03:28 +01:00
Ikey Doherty 9e400bd149 Use correct types in absence of stdatomic.h
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-18 14:37:07 +01:00
Ikey Doherty f5a65358af Use pthread mutex in the absence of stdatomic C11 atomics
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-18 14:33:26 +01:00
Ikey Doherty c82a007960 Add configure output, and check for stdatomics
Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
2016-05-18 14:18:56 +01:00
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
Patrick McCarty be74f893e6 Bump version for release 2015-10-20 10:24:49 -07:00
Patrick McCarty 7a5e4a9acd Do not log for non-fatal curl errors 2015-10-20 10:17:40 -07:00
Patrick McCarty 07a4c3d305 Bump version for release
This release fixes the handling of a server HTTP 304 response, now
treating it as non-fatal.
2015-10-02 12:15:07 -07:00
Patrick McCarty ab0e59628f Treat HTTP 304 codes as non-fatal
We set the If-Modified-Since header field for each GET request, and if
the condition fails, the server will respond with HTTP 304.

The 304 means the cached debuginfo is up-to-date, not requiring a fresh
download. Since this condition is non-fatal, avoid swapping the download
URLs in this case.
2015-10-02 12:15:02 -07:00
Patrick McCarty af4a3f64f0 Update gitignore 2015-10-02 10:36:39 -07:00
Patrick McCarty 8c65e96479 Bump version for release
This release fixes some compiler warnings and the distcheck target, and
addresses a recurrent 404 issue by avoiding a download attempt for
".tar" from the root debuginfo directory.
2015-09-29 12:32:23 -07:00
Patrick McCarty 7ea9a50b19 Avoid attempt to download {lib,src}/debug
The associated cache directories in /var/cache/debuginfo/{lib,src}
already exist, and the tar for / is not created on the server, so avoid
downloading it altogether.
2015-09-29 12:25:13 -07:00
Patrick McCarty e16ab76926 Refresh version in configure.ac
The configure.ac version was falling behind the tagged version...
2015-09-28 14:02:03 -07:00
Patrick McCarty 090cb27a34 Fix distcheck target
Adding two --with-* configure options for the systemd pkgconfig-defined
variables gives opportunity for distcheck to succeed; the variables use
hardcoded paths to /usr/lib/systemd/... without a PREFIX, so the paths
need to be relocated to $dc_install_base, a variable distcheck uses for
the normal variables that begin with PREFIX.
2015-09-28 13:52:24 -07:00
Patrick McCarty cbaf943fca Convert server to use modern Glib Thread API
Using a GStaticMutex and associated functions are deprecated, since
a GMutex can be statically allocated now (as of glib 2.32).
2015-09-25 14:20:22 -07:00
Patrick McCarty 32ae400e7d Update gitignore 2015-09-25 14:19:56 -07:00
Patrick McCarty 82f03f8e22 Fix build warning for implicitly included header 2015-09-25 14:00:33 -07:00
Arjan van de Ven c1f45d9f3c https 2015-07-20 23:02:09 -04:00
Arjan van de Ven bcff8fae39 default to the other server for now 2015-04-24 17:55:53 -04:00
Arjan van de Ven 66bab25bcd implement multi-url setup 2015-03-23 14:45:00 -04:00
20 changed files with 2516 additions and 916 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
...
+12 -1
View File
@@ -2,12 +2,14 @@
*.o *.o
clr_debug_fuse clr_debug_fuse
clr_debug_daemon clr_debug_daemon
clr_debug_prepare
.deps/ .deps/
.dirstamp
Makefile Makefile
Makefile.in Makefile.in
aclocal.m4 aclocal.m4
ar-lib
autom4te.cache/ autom4te.cache/
compile
config.h config.h
config.h.in config.h.in
config.log config.log
@@ -18,3 +20,12 @@ install-sh
missing missing
stamp-h1 stamp-h1
*.tar.gz *.tar.gz
tags
cscope.*
*.la
*.lo
.libs/
config.guess
config.sub
libtool
ltmain.sh
+37 -9
View File
@@ -1,19 +1,47 @@
EXTRA_DIST = COPYING clr_debug_fuse.service clr_debug_daemon.service debuginfo.conf EXTRA_DIST = COPYING clr_debug_fuse.service clr_debug_daemon.service clr_debug_daemon.socket debuginfo.conf
DISTCHECK_CONFIGURE_FLAGS = \
--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) \
--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
dist_bin_SCRIPTS = scripts/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
bin_PROGRAMS = clr_debug_fuse clr_debug_daemon clr_debug_prepare
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 = \
clr_debug_prepare_SOURCES = src/prepare.c -pthread \
$(AM_CFLAGS) \
$(LIBSYSTEMD_CFLAGS)
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 ${LIBSYSTEMD_LIBS}
clr_debug_daemon_LDADD = $(glib_LIBS) ${curl_LIBS}
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 clr_debug_daemon.socket
tmpfiles_DATA = debuginfo.conf tmpfiles_DATA = debuginfo.conf
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
+1
View File
@@ -1,5 +1,6 @@
[Unit] [Unit]
Description=Clear Linux debuginfo daemon Description=Clear Linux debuginfo daemon
DefaultDependencies=no
[Service] [Service]
Type=simple Type=simple
+9
View File
@@ -0,0 +1,9 @@
[Unit]
Description=Clear Linux OS debuginfo daemon
[Socket]
ListenStream=/run/clr-debug-info
SocketMode=0600
[Install]
WantedBy=sockets.target
+3 -1
View File
@@ -1,6 +1,8 @@
[Unit] [Unit]
Description=Clear Linux debuginfo fuse monitor Description=Clear Linux debuginfo fuse monitor
After=clr_debug_daemon.service After=clr_debug_daemon.socket
Requires=clr_debug_daemon.socket
DefaultDependencies=no
[Service] [Service]
Type=simple Type=simple
+45 -6
View File
@@ -2,22 +2,61 @@
# 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, 11, arjan@linux.intel.com) AC_INIT(clr-debug-info, 51, 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
AM_PROG_AR
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])
PKG_CHECK_MODULES([LIBSYSTEMD], [libsystemd])
LT_INIT
tmpfilesdir=`pkg-config --variable tmpfilesdir systemd` AC_DEFINE([SOCKET_PATH], ["/run/clr-debug-info"], [path to communication socket])
AC_SUBST(tmpfilesdir)
systemdsystemunitdir=`pkg-config --variable systemdsystemunitdir systemd` dir=""
AC_SUBST(systemdsystemunitdir) AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR],
[path to systemd system service dir @<:@default=/usr/lib/systemd/system@:>@]), [dir=${withval}],
[dir="$($PKG_CONFIG --variable=systemdsystemunitdir systemd)"])
test -z "${dir}" && dir=/usr/lib/systemd/system
AC_SUBST(systemdsystemunitdir, [${dir}])
dir=""
AC_ARG_WITH([systemdtmpfilesdir], AS_HELP_STRING([--with-systemdtmpfilesdir=DIR],
[path to systemd tmpfiles dir @<:@default=/usr/lib/tmpfiles.d@:>@]), [dir=${withval}],
[dir="$($PKG_CONFIG --variable=tmpfilesdir systemd)"])
test -z "${dir}" && dir=/usr/lib/tmpfiles.d
AC_SUBST(tmpfilesdir, [${dir}])
AC_CHECK_HEADER([stdatomic.h], [have_atomics="yes"], [have_atomics="no"])
if test x$have_atomics = "xyes"; then
AC_DEFINE([HAVE_ATOMIC_SUPPORT], [1], [stdatomic supported by compiler])
else
AC_MSG_WARN([C11 stdatomic support unavailable. Falling back to slow mutex])
fi
AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([Makefile])
AC_OUTPUT AC_OUTPUT
AC_MSG_RESULT([
clr-debuginfo $VERSION
prefix: ${prefix}
libdir: ${libdir}
sysconfdir: ${sysconfdir}
exec_prefix: ${exec_prefix}
bindir: ${bindir}
datarootdir: ${datarootdir}
compiler: ${CC}
cflags: ${CFLAGS}
ldflags: ${LDFLAGS}
systemd-unit-dir: ${systemdsystemunitdir}
tmpfiles.d: ${tmpfilesdir}
C11 stdatomic support: ${have_atomics}
])
+4 -2
View File
@@ -8,5 +8,7 @@
# See tmpfiles.d(5) for details # See tmpfiles.d(5) for details
# Clear tmp directories separately, to make them easier to override # Clear tmp directories separately, to make them easier to override
d /var/cache/debuginfo/lib 755 root root 10d # Unfortunatly tmpfiles doesn't change the ownership for things if they
d /var/cache/debuginfo/src 755 root root 1d # are not listed.
d /var/cache/debuginfo/lib 755 dbginfo dbginfo 10d
d /var/cache/debuginfo/src 755 dbginfo dbginfo 1d
+146
View File
@@ -0,0 +1,146 @@
#!/bin/bash
#
# Clear Linux -- automatic debuginfo preparation
#
# Copyright 2019 Intel Corporation
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, version 3 or later of the License.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
if [ $# -eq 1 ] || [ $# -gt 2 ]; then
echo "Usage: $0 [SOURCEDIR] [DESTDIR]"
echo "Generates automatic debuginfo tarballs in DESTDIR using content from SOURCEDIR."
echo "The default SOURCEDIR and DESTDIR are /var/www/html/debuginfo.raw and"
echo "/var/www/html/debuginfo, respectively."
exit 0
fi >&2
if [ $# -eq 2 ]; then
if [ ! -d "$1" ]; then
echo "ERROR: SOURCEDIR argument must be an existing directory"
exit 1
fi
fi >&2
export SRC="${1:-/var/www/html/debuginfo.raw}"
export DEST="${2:-/var/www/html/debuginfo}"
srclist=$(mktemp -p .)
destlist=$(mktemp -p .)
trap "rm $srclist $destlist" EXIT
set -o pipefail
# First, scan the state of the SRC tree, containing all content for debuginfo RPMs.
if ! find "$SRC" -mindepth 4 -printf "%p %T@ %y\n" | sed "s|$SRC||" | sort -k 1 > "$srclist"; then
echo "ERROR: expected content in $SRC"
exit 1
fi
# Next, scan the state of the DEST tree, containing previously generated
# automatic debuginfo tarballs. Will be empty on first run.
mkdir -p "$DEST"
find "$DEST" -mindepth 2 -name '*.tar' -printf "%p %T@ %y\n" | sed "s|$DEST||;s|\.tar | |" | sort -k 1 > "$destlist"
process_one() {
srcname="$1"
destname="$2"
filetype="$3"
srcloc="$SRC/$srcname"
srcdir="$SRC/$(echo "$srcname" | sed -r 's@^(/usr/(lib|share|src)/debug(/src)?).*@\1@')"
tarcontent="$(echo "$srcname" | sed -r 's@^/usr/(lib|share|src)/debug(/src)?@.@')"
destdir="$DEST/$(dirname "$destname")"
dest="$DEST/$destname.tar"
echo "Creating $destname.tar ..."
mkdir -p "$destdir"
if [ "$filetype" = "f" ]; then
tar --no-recursion -C "$srcdir" --zstd -cf "$dest" "$tarcontent"
elif [ "$filetype" = "l" ]; then
# We "unsymlink" non-broken symlinks as an optimization by adding the
# symlink target to the dest tarball, transforming the name as appropriate.
if target=$(realpath -e "$srcloc" --relative-to="$srcdir" 2> /dev/null); then
tar --no-recursion -C "$srcdir" --zstd -cf "$dest" --transform='s|.*|'"$tarcontent"'|' "$target"
else
tar --no-recursion -C "$srcdir" --zstd -cf "$dest" "$tarcontent"
fi
elif [ "$filetype" = "d" ]; then
tar --no-recursion -C "$srcdir" --zstd -cf "$dest" "$tarcontent"
fi
}
export -f process_one
gawk '
BEGIN { OFS = "\t" }
LIST == "src" {
name = $1
srcname = name
ret = sub(/^\/usr\/lib\/debug/, "/lib", name)
if (ret == 0) {
ret = sub(/^\/usr\/src\/debug/, "/src", name)
if (ret == 0) {
ret = sub(/^\/usr\/share\/debug\/src/, "/src", name)
if (ret == 0) {
ret = sub(/^\/usr\/share\/debug/, "/lib", name)
if (ret == 0) {
print "Error: unexpected debuginfo file:", name
exit 1
}
}
}
}
src[name]["srcname"] = srcname
src[name]["time"] = $2
src[name]["type"] = $3
src[name]["done"] = 0
}
LIST == "dest" {
dest_content = 1
name = $1
time = $2
type = $3
if (name in src) {
# No need to recreate directory tars if they exist
if (src[name]["type"] == "d") {
src[name]["done"] = 1
next
} else {
# Only regenerate content if timestamp is newer
if (time < src[name]["time"]) {
print src[name]["srcname"], name, src[name]["type"]
}
src[name]["done"] = 1
}
}
}
END {
PROCINFO["sorted_in"] = "@ind_str_asc"
if (dest_content == 0) {
for (s in src) {
print src[s]["srcname"], s, src[s]["type"]
}
} else {
for (s in src) {
if (src[s]["done"] == 0) {
print src[s]["srcname"], s, src[s]["type"]
}
}
}
}
' LIST="src" "$srclist" LIST="dest" "$destlist" \
| parallel --colsep '\t' process_one
# vi: ft=sh et sw=2 sts=2
+76 -68
View File
@@ -7,36 +7,40 @@
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 or later of the License. * the Free Software Foundation, version 2 or later of the License.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* 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>
#include "config.h"
/* 0.75 seconds timeout */ /* 0.75 seconds timeout */
#define TIMEOUT 750000 #define TIMEOUT 75000
#define TIMEOUT2 15000 #define TIMEOUT2 1500
#define TIMEOUT3 500 #define TIMEOUT3 500
char *prefix = "src"; char *prefix = "src";
@@ -45,71 +49,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);
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0)
return;
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, ":clr-debug-info");
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); // printf("Trying to aquire %s\n", path);
if (ret < 0) {
printf("Cannot connect %s\n", strerror(errno));
close(sockfd);
return;
}
len = sizeof(cred); sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
memset(&cred, 0, sizeof(cred)); if (sockfd < 0) {
ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len); return;
}
if (ret == 0 && cred.pid == pid) { sun.sun_family = AF_UNIX;
printf("Recursion\n"); strcpy(sun.sun_path, SOCKET_PATH);
close(sockfd);
}
command = NULL; ret = connect(sockfd,
if (asprintf(&command, "%llu:%s:%s", (unsigned long long)timestamp, prefix, path) < 0) { (struct sockaddr *)&sun,
close(sockfd); offsetof(struct sockaddr_un, sun_path) + strlen(SOCKET_PATH) + 1);
return; if (ret < 0) {
} printf("Cannot connect to %s: %s\n", SOCKET_PATH, strerror(errno));
write(sockfd, command, strlen(command )+1); close(sockfd);
return;
}
FD_ZERO(&rfds); len = sizeof(cred);
FD_SET(sockfd, &rfds); memset(&cred, 0, sizeof(cred));
ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cred, &len);
/* Wait up to 0.5 seconds. */ if (ret == 0 && cred.pid == pid) {
tv.tv_sec = 0; printf("Recursion\n");
tv.tv_usec = TIMEOUT; close(sockfd);
return;
/* if we had a timeout recently, must go quicker to avoid sequential delays */ }
if (deadtime > time(NULL)) {
tv.tv_usec = TIMEOUT2;
shorttime = 1;
}
ret = 1; command = NULL;
if (asprintf(&command, "%llu:%s:%s", (unsigned long long)timestamp, prefix, path) < 0) {
close(sockfd);
return;
}
wr = write(sockfd, command, strlen(command) + 1);
if (!timestamp) FD_ZERO(&rfds);
ret = select(sockfd+1, &rfds, NULL, &rfds, &tv); FD_SET(sockfd, &rfds);
if (ret == 0 && !shorttime) {
printf("timeout for %s\n", path);
deadtime = time(NULL) + 4;
}
close(sockfd); /* Wait up to 0.5 seconds. */
tv.tv_sec = 0;
tv.tv_usec = TIMEOUT;
/* if we had a timeout recently, must go quicker to avoid sequential delays */
if (deadtime > time(NULL)) {
tv.tv_usec = TIMEOUT2;
shorttime = 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;
}
close(sockfd);
} }
+451 -414
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;
goto end;
}
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:
*/
+493
View File
@@ -0,0 +1,493 @@
/*
* 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 int 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) {
if (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:
*/
-217
View File
@@ -1,217 +0,0 @@
/*
* Fenrus Linux -- automatic debug information installation
* Clear Linux -- automatic debug information installation
* Server side preparation logic
*
* Copyright (C) 2013 Arjan van de Ven
* Copyright (C) 2014 Intel Corporation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 or later of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Arjan van de Ven <arjanvandeven@gmail.com>
*
*/
#define _GNU_SOURCE
#include <stdio.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 <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <libgen.h>
#include <glib.h>
/*
this program takes a directory /var/www/html/debuginfo.raw,
in which all debuginfo rpms have previously been rpm2cpio'd, and
creates the content for /var/www/html/debuginfo
which contains tar'd up/compressed versions of all the files.
As part of this symlinks will be resolved server side
as much as possible.
*/
static void unsymlink(char *filename)
{
char *target;
target = canonicalize_file_name(filename);
// sleep(1);
// printf("Symlink %s points to %s\n", filename, target);
unlink(filename);
link(target, filename);
}
static void wait_for_loadavg(void)
{
FILE *file;
char line[4096];
while (1) {
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);
}
}
int tarcount;
static void do_one_file(char *base1, char *base2, char *path, int isdir)
{
char *fullpath1 = NULL, *fullpath2 = NULL, *dir, *dir2;
struct stat buf1, buf2;
int ret;
if (asprintf(&fullpath1, "%s%s", base1, path) < 0)
return;
if (asprintf(&fullpath2, "%s%s.tar", base2, path) < 0) {
free(fullpath1);
return;
}
ret = lstat(fullpath1, &buf1);
if (ret)
goto out;
if (S_ISLNK(buf1.st_mode)) {
// printf("%s is a symlink\n", fullpath1);
ret = stat(fullpath1, &buf2);
if (!ret)
unsymlink(fullpath1);
ret = lstat(fullpath1, &buf1);
}
ret = lstat(fullpath2, &buf2);
if (ret || buf1.st_mtime > buf2.st_mtime) {
char *command = NULL;
unlink(fullpath2);
dir = strdup(fullpath2);
dir2 = dirname(dir);
if (asprintf(&command, "mkdir -p %s", dir2) >= 0) {
// printf("Running -%s-\n", command);
system(command);
free(command);
}
free(dir);
chdir(base1);
if (!isdir && asprintf(&command, "tar --no-recursion -C %s -Jcf %s %s &", base1, fullpath2, path) >= 0) {
// 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) {
// printf("Running -%s-\n", command);
printf("Processing %s\n", fullpath2);
tarcount++;
if (tarcount > 50) {
wait_for_loadavg();
tarcount = 0;
}
system(command);
free(command);
}
}
out:
free(fullpath1);
free(fullpath2);
}
static void recurse_dir(char *base1, char *base2, char *path)
{
DIR *dir;
char *fullpath1, *fullpath2, *newpath;
struct dirent *entry;
fullpath1 = NULL;
fullpath2 = NULL;
if (asprintf(&fullpath1, "%s%s", base1, path) < 0)
return;
dir = opendir(fullpath1);
if (!dir) {
free(fullpath1);
return;
}
while (1) {
entry = readdir(dir);
if (!entry)
break;
if (strcmp(entry->d_name, ".") == 0)
continue;
if (strcmp(entry->d_name, "..") == 0)
continue;
newpath = NULL;
fullpath2 = NULL;
asprintf(&fullpath2, "%s/%s", fullpath1, entry->d_name);
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)) {
do_one_file(base1, base2, newpath, 1);
recurse_dir(base1, base2, newpath);
} else {
do_one_file(base1, base2, newpath, 0);
}
free(newpath);
}
}
closedir(dir);
free(fullpath1);
}
int main(int argc, char **argv)
{
if (argc < 0)
(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/src/debug/", "/var/www/html/debuginfo/src/", ".");
return EXIT_SUCCESS;
}
+470 -198
View File
@@ -2,281 +2,553 @@
* Clear Linux -- automatic debug information installation * Clear Linux -- automatic debug information installation
* *
* Copyright (C) 2013 Arjan van de Ven * Copyright (C) 2013 Arjan van de Ven
* Curl portions borrowed from the Fenrus Update code * Curl portions borrowed from the Fenrus Update code
* which in part is (C) 2012 Intel Corporation * which in part is (C) 2012 Intel Corporation
* Copyright (C) 2014 Intel Corporation * Copyright (C) 2014 Intel Corporation
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 or later of the License. * the Free Software Foundation, version 3 or later of the License.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* 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 <grp.h>
#include <linux/capability.h>
#include <malloc.h>
#include <pthread.h>
#include <pwd.h>
#include <libgen.h>
#include <signal.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/prctl.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>
GStaticMutex dupes_mutex = G_STATIC_MUTEX_INIT; #include "systemd/sd-daemon.h"
#include "config.h"
static GHashTable *hash; #ifdef HAVE_ATOMIC_SUPPORT
#include <stdatomic.h>
#endif
#define TIMEOUT 600 /* 10 minutes */
static pthread_mutex_t dupes_mutex = PTHREAD_MUTEX_INITIALIZER;
char *urls[2] = { "https://cdn.download.clearlinux.org/debuginfo/",
"https://cdn-alt.download.clearlinux.org/debuginfo/" };
int urlcounter = 1;
static NcHashmap *hash = NULL;
#define MAX_CONNECTIONS 16
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_static_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_static_mutex_unlock(&dupes_mutex); pthread_mutex_unlock(&dupes_mutex);
return retval; return retval;
} }
#ifdef HAVE_ATOMIC_SUPPORT
static atomic_int current_connection_count = 0;
/**
* 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);
}
#else /* HAVE_ATOMIC_SUPPORT */
static int current_connection_count = 0;
/* No stdatomic compiler support, fallback to pthread mutex (slower) */
pthread_mutex_t con_count_mutex = PTHREAD_MUTEX_INITIALIZER;
/**
* Get the current connection count via mutex
*/
__nc_inline__ static inline int get_current_connection_count(void)
{
int r;
pthread_mutex_lock(&con_count_mutex);
r = current_connection_count;
pthread_mutex_unlock(&con_count_mutex);
return r;
}
/**
* Increment the connection counter via mutex
*/
__nc_inline__ static inline void inc_connection_count(void)
{
pthread_mutex_lock(&con_count_mutex);
current_connection_count++;
pthread_mutex_unlock(&con_count_mutex);
}
/**
* Decrement the connection counter via mutex
*/
__nc_inline__ static inline void dec_connection_count(void)
{
pthread_mutex_lock(&con_count_mutex);
current_connection_count--;
pthread_mutex_unlock(&con_count_mutex);
}
#endif /* !(HAVE_ATOMIC_SUPPORT) */
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; long changed;
char filename[PATH_MAX]; int fd;
CURL *curl = NULL; autofree(char) *filename = NULL;
FILE *file; CURL *curl = NULL;
struct stat statbuf; FILE *file;
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(); // fprintf(stderr, "Fetching %s, prefix %s, path %s\n", url, prefix, path);
if (curl == NULL) if (asprintf(&filename, "/tmp/clr-debug-info-XXXXXX") < 0) {
return 301; curl_easy_cleanup(curl);
return 418;
}
fd = mkstemp(filename);
if (fd < 0) {
curl_easy_cleanup(curl);
return 418;
}
file = fdopen(fd, "w");
strcpy(filename, "/tmp/clr-debug-info-XXXXXX"); curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
fd = mkstemp(filename); curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
if (fd < 0) {
curl_easy_cleanup(curl);
return 500;
}
file = fdopen(fd, "w"); /*
* Some sane timeout values to prevent stalls
*
* Connect timeout is for first connection to the server.
* The low speed timeout is for slow downloads. Since many
* files are several mB large, we want to prevent them from
* taking forever. (1kB/sec avg over 30secs).
*/
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 30);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1024);
curl_easy_setopt(curl, CURLOPT_URL, url); /* request timestamp of files from server */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); curl_easy_setopt(curl, CURLOPT_FILETIME, 1);
if (timestamp) { if (timestamp) {
curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
curl_easy_setopt(curl, CURLOPT_TIMEVALUE, timestamp); curl_easy_setopt(curl, CURLOPT_TIMEVALUE, timestamp);
} }
code = curl_easy_perform(curl); code = curl_easy_perform(curl);
/* can't trust the file if we get an error back */ /* can't trust the file if we get an error back */
if (code != 0) if (code != 0) {
unlink(filename); unlink(filename);
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
fflush(file);
// printf("HTTP return code is %i\n", ret);
if (ret == 200) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &ret);
char *command = NULL; fflush(file);
// printf("Filename is %s\n", filename); // printf("HTTP return code is %i\n", ret);
memset(&statbuf, 0, sizeof(statbuf)); /* HTTP 304 is returned if (a) the cached debuginfo has the same
stat(filename, &statbuf); * timestamp or is newer than that on the server and (b) we haven't
if (statbuf.st_size > 0 && asprintf(&command, "tar -C /var/cache/debuginfo/%s --no-same-owner -xf %s", prefix, filename) >= 0) { * already added the URL to the hash table. So, the first crash for a
system(command); * boot may result in a 304 if the debuginfo had been downloaded in a
free(command); * previous boot.
} */
} if (ret != 200 && ret != 404 && ret != 304) {
unlink(filename); urlcounter++;
curl_easy_cleanup(curl); }
fclose(file);
return ret; if (ret == 200) {
autofree(char) *command = NULL;
struct stat statbuf;
/* get timestamp, if any */
curl_easy_getinfo(curl, CURLINFO_FILETIME, &changed);
if (changed >= 0) {
struct timespec times[2];
times[0].tv_sec = (time_t)changed;
times[0].tv_nsec = 0;
times[1].tv_sec = (time_t)changed;
times[1].tv_nsec = 0;
futimens(fd, times);
}
memset(&statbuf, 0, sizeof(statbuf));
stat(filename, &statbuf);
if (statbuf.st_size <= 0) {
ret = 418;
goto out;
}
/* test extraction first */
if (asprintf(&command, "tar -C /var/cache/debuginfo/%s --no-same-owner "
"--no-same-permissions -tf %s",
prefix,
filename) < 0) {
ret = 418;
goto out;
}
if (system(command) != 0) {
ret = 418;
fprintf(stderr, "Error: tar validation failed\n");
goto out;
}
free(command); /* reuse */
if (asprintf(&command, "tar -C /var/cache/debuginfo/%s --no-same-owner "
"--no-same-permissions -xf %s",
prefix,
filename) < 0) {
ret = 418;
goto out;
}
if (system(command) != 0) {
ret = 418;
fprintf(stderr, "Error: tar extraction failed\n");
goto out;
}
}
out:
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)); memset(buf, 0, sizeof(buf));
ret = read(fd, buf, PATH_MAX+8); ret = read(fd, buf, PATH_MAX + 8);
if (ret < 0) { if (ret < 0) {
close(fd); goto thread_end;
return NULL; }
} c = strchr(buf, ':');
prefix = buf; if (!c) {
c = strchr(buf, ':'); goto thread_end;
if (!c) { }
close(fd); *c = 0;
return NULL; timestamp = strtoull(buf, NULL, 10);
} c++;
*c = 0; prefix = c;
timestamp = strtoull(buf, NULL, 10); path = strchr(c, ':');
c++; if (!path) {
prefix = c; goto thread_end;
path = strchr(c, ':'); }
if (!path) { *path = 0;
close(fd); path++;
return NULL;
}
*path = 0;
path++;
/* GDB and elfutils both stat /usr/lib/debug directly when looking up
* debuginfo, so avoid the download for "/.tar"; the associated cache
* directories already exist by this point.
*/
if (strlen(path) == 1 && strcmp(path, "/") == 0) {
goto thread_end;
}
if (strstr(path, "..") || strstr(prefix, "..") || strstr(path, "'") || strstr(path, ";")) { if (strstr(path, "..") || strstr(prefix, "..") || strstr(path, "'") || strstr(path, ";")) {
close(fd); goto thread_end;
return NULL; }
}
if (strcmp(prefix, "lib") !=0 && strcmp(prefix, "src") != 0) { if (strcmp(prefix, "lib") != 0 && strcmp(prefix, "src") != 0) {
/* invalid prefix */ /* invalid prefix */
close(fd); goto thread_end;
return NULL; }
} url = NULL;
url = NULL; if (asprintf(&url, "%s%s%s.tar", urls[urlcounter % 2], prefix, path) < 0) {
if (asprintf(&url, "http://debuginfo.clearlinux.org/debuginfo/%s%s.tar", prefix, path) < 0) { goto thread_end;
close(fd); }
return NULL;
}
// printf("Getting url %s %i:%06i\n", url, before.tv_sec, before.tv_usec); // printf("Getting url %s %i:%06i\n", url, before.tv_sec, before.tv_usec);
ret = curl_get_file(url, prefix, timestamp); ret = curl_get_file(url, prefix, timestamp);
if (ret != 200) switch (ret) {
printf("Request for %s resulted in error %i\n", url, ret); case 200:
case 300:
case 304:
case 404:
// ignore these error codes
break;
default:
fprintf(stderr, "Request for %s resulted in error %i\n", url, ret);
break;
}
gettimeofday(&after, NULL); gettimeofday(&after, NULL);
if (timedelta(before, after) > 0.6) #if 0
printf("Request for %s took %5.2f seconds (%i - %i)\n", url, if (timedelta(before, after) > 0.6)
after.tv_sec - before.tv_sec + (1.0*after.tv_usec - before.tv_usec) / 1000000.0, ret, (int)timestamp); 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);
#endif
/* tell the other side we're done with the download */
wr = write(fd, "ok", 3);
/* 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;
uid_t dbg_user = 0;
gid_t dbg_group = 0;
struct passwd *passwdentry;
const char *required_paths[] = { "/var/cache/debuginfo/lib", "/var/cache/debuginfo/src" };
if (argc < -1) umask(0);
(void)argv; passwdentry = getpwnam("dbginfo");
if (passwdentry) {
dbg_user = passwdentry->pw_uid;
dbg_group = passwdentry->pw_gid;
}
endpwent();
if (prctl(PR_SET_DUMPABLE, 0) != 0) {
fprintf(stderr,
"Failed to disable PR_SET_DUMPABLE. Do NOT gdb attach this process: %s\n",
strerror(errno));
}
if (geteuid() == 0) {
if (prctl(PR_CAPBSET_DROP, CAP_SYS_ADMIN) != 0) {
fprintf(stderr, "Failed to drop caps: %s\n", strerror(errno));
}
}
for (size_t i = 0; i < ARRAY_SIZE(required_paths); i++) {
const char *req_path = required_paths[i];
struct stat st = { .st_ino = 0 };
if (lstat(req_path, &st) == 0) {
/* If the file already exists, check ownership
* and delete tree if incorrect. Essentially a
* one-off operation to transition from root owned to
* dbginfo owned */
if (st.st_uid == dbg_user) {
continue;
}
fprintf(stderr, "Removing old debug information %s\n", req_path);
nc_rm_rf(req_path);
}
if (!nc_mkdir_p(req_path, 00755)) {
fprintf(stderr, "Failed to mkdir: %s %s\n", strerror(errno), req_path);
return EXIT_FAILURE;
}
if (chown(req_path, dbg_user, dbg_group) != 0) {
fprintf(stderr, "Failed to chown: %s %s\n", strerror(errno), req_path);
return EXIT_FAILURE;
}
}
signal(SIGPIPE, SIG_IGN);
if (sd_listen_fds(0) == 1) {
/* systemd socket activation */
sockfd = SD_LISTEN_FDS_START + 0;
} else if (sd_listen_fds(0) > 1) {
fprintf(stderr, "Too many file descriptors received.\n");
exit(EXIT_FAILURE);
} else {
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0) {
exit(EXIT_FAILURE);
}
sun.sun_family = AF_UNIX;
strcpy(sun.sun_path, SOCKET_PATH);
ret = bind(sockfd,
(struct sockaddr *)&sun,
offsetof(struct sockaddr_un, sun_path) + strlen(SOCKET_PATH) + 1);
if (ret < 0) {
fprintf(stderr, "Failed to bind:%s \n", strerror(errno));
exit(EXIT_FAILURE);
}
if (listen(sockfd, 16) < 0) {
fprintf(stderr, "Failed to listen:%s \n", strerror(errno));
exit(EXIT_FAILURE);
}
}
if (setgid(dbg_group)) {
fprintf(stderr, "Unable to drop privileges setgid %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (setgroups(1, &dbg_group)) {
fprintf(stderr, "Unable to drop privileges setgroups %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (setuid(dbg_user)) {
fprintf(stderr, "Unable to drop privileges setuid %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
while (1) {
fd_set rfds;
struct timeval tv;
int ret;
int clientsock;
pthread_t thread;
malloc_trim(0);
/* use select() to timeout and exit gracefully */
FD_ZERO(&rfds);
FD_SET(sockfd, &rfds);
tv.tv_sec = TIMEOUT;
tv.tv_usec = 0;
ret = select(sockfd + 1, &rfds, NULL, NULL, &tv);
if (ret == -1) {
perror("select()");
exit(EXIT_FAILURE);
} else if (ret == 0) {
break;
}
signal(SIGPIPE,SIG_IGN); clientsock = accept(sockfd, NULL, NULL);
if (access("/var/cache/debuginfo/lib/", F_OK)) /* Too many connections, wait for the next loop/retry */
system("mkdir -p /var/cache/debuginfo/lib/ &> /dev/null"); if (get_current_connection_count() >= MAX_CONNECTIONS) {
if (access("/var/cache/debuginfo/src/", F_OK)) /* printf("Too many connections!\n"); */
system("mkdir -p /var/cache/debuginfo/src/ &> /dev/null"); shutdown(clientsock, SHUT_RDWR);
close(clientsock);
continue;
}
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if (!curl_done) {
if (sockfd < 0) curl_global_init(CURL_GLOBAL_ALL);
return EXIT_FAILURE; curl_done = 1;
}
sun.sun_family = AF_UNIX; /* Update the connection count and start the new thread */
strcpy(sun.sun_path, ":clr-debug-info"); inc_connection_count();
sun.sun_path[0] = 0; /* anonymous unix socket */ pthread_create(&thread, NULL, server_thread, (void *)(unsigned long)clientsock);
pthread_detach(thread);
ret = bind(sockfd, (struct sockaddr *)&sun, 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;
}
if (listen(sockfd, 16) < 0) {
printf("Failed to listen:%s \n", strerror(errno));
return EXIT_FAILURE;
}
while (1) {
int clientsock;
pthread_t thread;
malloc_trim(0);
clientsock = accept(sockfd, NULL, NULL);
if (!curl_done) {
curl_global_init(CURL_GLOBAL_ALL);
curl_done = 1;
}
pthread_create(&thread, NULL, server_thread, (void *)(unsigned long)clientsock);
pthread_detach(thread);
}
close(sockfd);
if (hash) {
nc_hashmap_free(hash);
}
} }
+2
View File
@@ -0,0 +1,2 @@
#!/bin/bash
clang-format -i $(find . -name '*.[ch]')