Compare commits

...
51 Commits
Author SHA1 Message Date
Joachim Wiberg b69d8a6013 Fix copyfile() not preserving source mode with restrictive umask
copyfile() passes the source mode to open(2), which applies the umask,
so a 0644 file copied with umask 0027 ends up 0640.

Apply the source mode with fchmod() after creating the destination.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-09-13 18:53:40 +02:00
Joachim Wiberg e874608b76 Fix mkpath() ignoring mode when umask is more restrictive
mkpath(dir, 0755) with umask 0027 silently creates a 0750 directory,
since mkdir(2) applies the umask to the requested mode.  Callers pass
an explicit mode and expect exactly that.

Apply the mode with chmod() after each successful mkdir().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-09-13 17:17:37 +02:00
Joachim Wiberg c1e050f93c Update ChangeLog and bump version for v2.6.2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-22 21:59:46 +01:00
Joachim Wiberg 0e45929323 Make note of possible extensions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-22 21:44:20 +01:00
Joachim Wiberg 958502d982 Fix memory leak in which() on realloc() failure
Avoid leaking memory when realloc() fails by using a temporary pointer
and only updating the original on success. Also remove redundant NULL
check before free().

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-22 20:48:35 +01:00
Joachim Wiberg f0cb0b7bf2 .github: bump actions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-04-28 08:34:00 +02:00
Joachim Wiberg ee5788d49b Create SECURITY.md policy 2025-04-28 07:54:35 +02:00
Joachim Wiberg 9bfe54f959 pidfile(): ensure prefix path ends with slash
For some builds using Buildroot, the system default _PATH_VARRUN did not
include a trailing slas '/', which cause the pidfile() function to fail.

Example:
  pidfile("arg") => "/var/runarg.pid"

Since the __pidfile_path can be overridden we add a check if that prefix
path ends with '/', if not we add one in the path composition.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2024-03-25 05:23:41 +01:00
Joachim Wiberg 960e7f69ed Update changelog and bump version for v2.6.1 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-15 10:22:36 +02:00
Joachim Wiberg 68bc83f08f FTBFS uClibc and musl libc, lite.h missing stdarg.h include
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-15 10:21:30 +02:00
Joachim Wiberg 986d29caf0 m4: sync with latest ax_prog_doxygen.m4
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-10-15 10:18:33 +02:00
Joachim Wiberg 2d9f8f595d Update ChangeLog and bump version to v2.6.0 GA
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 16:59:24 +02:00
Joachim Wiberg 3f8ad99f1f Fix varargs bug/usage, found by Coverity Scan
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:47:59 +02:00
Joachim Wiberg 63e25ecae4 .github: use new syntax for setting variables
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:47:43 +02:00
Joachim Wiberg 6c53ee819b test: adjust times again
Running the tests in a Debian container seem to be taking way too long.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:26:08 +02:00
Joachim Wiberg da21e02be0 Update ChangeLog diffs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:19:13 +02:00
Joachim Wiberg c33500c372 .github: import release job from Mg project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:17:53 +02:00
Joachim Wiberg bed65c84e6 .github: bump action versions, node12 deprecated
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:17:27 +02:00
Joachim Wiberg c4f262b721 debian: prepare new release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:17:14 +02:00
Joachim Wiberg 563b387ec7 test: increase delay waiting for background job in Debian container
GitHub action fails, probably due to too short wait delay.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:11:51 +02:00
Joachim Wiberg b93d74c065 Update ChangeLog and bump version for v2.6.0-rc1
Bump ABI: 5.3.2 -> 5.4.0

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 12:00:04 +02:00
Joachim Wiberg c1f7bc2bbb Update copyright years
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 11:56:26 +02:00
Joachim Wiberg 99f644b512 doc: document new procfile functions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 11:48:37 +02:00
Joachim Wiberg 92bb0ca399 runbg: new helper function inspired by the Infix project
The runbg() function is a delayed execvp() that has double-forked to the
background (to not create zombies).  It is useful for running commands
in the background for APIs that need to return a reply, e.g. a NETCONF
RPC to reboot the system.

Compared to the original in Infix, this version double forks to reparent
itself to PID 1 (init), closes all open files, including stdio.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 11:00:09 +02:00
Joachim Wiberg 921afdecb4 Minor, indent
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 10:49:25 +02:00
Joachim Wiberg 47b02df823 Minor, spellcheck
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-09-17 10:49:13 +02:00
Joachim Wiberg 4dd14dac42 New helper functions to read/write /proc and /sys style files
From the Infix project.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 13:19:05 +02:00
Joachim Wiberg 97a3d43e9c Reminder
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 12:47:49 +02:00
Joachim Wiberg 784a667584 New helper function, fexistf(), made for the Infix project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 12:18:45 +02:00
Joachim Wiberg 1f6ff90d9e New helper function, vfopenf(), inspired by the Infix project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 12:16:58 +02:00
Joachim Wiberg 9eb847e56c New helper function, popenf(), from the Infix project
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 12:03:24 +02:00
Joachim Wiberg 55272c13f7 Start new devel cycle for v2.6.0 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-06-11 11:38:01 +02:00
Joachim Wiberg 34097d33b6 Update changelog and bump version for v2.5.3 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 07:05:58 +02:00
Joachim Wiberg 311288eec9 test: major refactor and cleanup
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 06:51:25 +02:00
Joachim Wiberg 63cf8867c5 test: refactor, each test in separate func
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 06:00:47 +02:00
Joachim Wiberg ac7b4194cf Fix copyfile() regression introduced in 4e9b5c13
Caught in release cycle.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 05:55:48 +02:00
Joachim Wiberg e44aaa4fd5 test: slight refactor to add diff -q A B check to fcopyfile()
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 05:55:48 +02:00
Joachim Wiberg 97cb2cb87c test: reproduce copyfile() off by one regression
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-08 05:55:39 +02:00
Joachim Wiberg 837c3f23be Prepare for v2.5.3 bug fix release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-02 22:20:03 +02:00
Joachim Wiberg 4e9b5c13c1 copyfile: allow copying empty files to non-existing destination dirs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-02 22:17:47 +02:00
Joachim Wiberg d61b847e55 rsync: fix check for missing dst when dst is a directory
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-02 22:17:09 +02:00
Joachim Wiberg 90fdcdeb39 rsync: fix return value when copying a single file
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-02 22:16:50 +02:00
Joachim Wiberg e36ac8f462 test: finalize rsync unit test to reproduce bugs
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2023-04-02 22:16:21 +02:00
Joachim Wiberg eb3bb94cfa Update changelog and bump version for v2.5.2 release
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 14:19:11 +02:00
Joachim Wiberg 11b9c88732 Add printf format attribute to all vararg functions
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2022-04-17 14:11:31 +02:00
Joachim Wiberg 11d4aafce1 Merge pull request #20 from yangfl/upstream
Fix for some non-linux
2022-01-27 11:34:50 +01:00
yangfl 5cdc5736f3 Replace mknod() with creat()
For mknod(), mode must be S_IFIFO otherwise the behaviour is unspecified
according to SUS, and apparently Debian/kFreeBSD is in the hater list.
2022-01-27 00:11:00 +08:00
yangfl 059bfd7806 test: use sleep() for mtime delay
For filesystems without high-resolution time support, an usleep() is too short
to alter mtime. Though 1s is quite safe, sleep for 2s in case it gets awaken
right within that second.
2022-01-26 23:57:27 +08:00
yangfl 0cda6339a4 Fix fisdir sigfault on Hurd
Apparently Hurd's stat() does not like NULL and will sigfault, this patch takes
care of it.
2022-01-26 02:11:08 +08:00
Joachim Wiberg 8120492fc8 Update changelogs and bump version for v2.5.1
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-12-02 00:43:02 +01:00
Joachim Wiberg 2065165ae9 debian: use dh_link(1) instead to create /usr/inclute/lite symlink
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2021-12-02 00:42:19 +01:00
35 changed files with 1069 additions and 179 deletions

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
# Security Policy
## Supported Versions
libite (-lite) is a small project, as such we have no possibility to support older versions.
The only supported version is the latest released on GitHub:
<https://github.com/troglobit/libite/releases>
## Reporting a Vulnerability
Contact the project's main author and owner to report and discuss vulnerabilities.
+3 -3
View File
@@ -28,7 +28,7 @@ jobs:
run: | run: |
sudo apt-get -y update sudo apt-get -y update
sudo apt-get -y install tree doxygen sudo apt-get -y install tree doxygen
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Configure - name: Configure
run: | run: |
./autogen.sh ./autogen.sh
@@ -44,7 +44,7 @@ jobs:
run: | run: |
make check || (cat test/test-suite.log; false) make check || (cat test/test-suite.log; false)
- name: Upload Test Results - name: Upload Test Results
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: libite-test-${{ matrix.compiler }} name: libite-test-${{ matrix.compiler }}
path: test/* path: test/*
@@ -55,7 +55,7 @@ jobs:
env: env:
MAKEFLAGS: -j3 MAKEFLAGS: -j3
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Installing dependencies - name: Installing dependencies
run: | run: |
apt-get update apt-get update
+4 -6
View File
@@ -15,7 +15,7 @@ jobs:
coverity: coverity:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Fetch latest Coverity Scan MD5 - name: Fetch latest Coverity Scan MD5
id: var id: var
env: env:
@@ -24,10 +24,8 @@ jobs:
wget -q https://scan.coverity.com/download/cxx/linux64 \ wget -q https://scan.coverity.com/download/cxx/linux64 \
--post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \ --post-data "token=$TOKEN&project=${COVERITY_PROJ}&md5=1" \
-O coverity-latest.tar.gz.md5 -O coverity-latest.tar.gz.md5
export MD5=$(cat coverity-latest.tar.gz.md5) echo "md5=$(cat coverity-latest.tar.gz.md5)" | tee -a $GITHUB_OUTPUT
echo "Got MD5 $MD5" - uses: actions/cache@v4
echo ::set-output name=md5::${MD5}
- uses: actions/cache@v2
id: cache id: cache
with: with:
path: coverity-latest.tar.gz path: coverity-latest.tar.gz
@@ -76,7 +74,7 @@ jobs:
--form description="${PROJECT_NAME} $(git rev-parse HEAD)" \ --form description="${PROJECT_NAME} $(git rev-parse HEAD)" \
https://scan.coverity.com/builds?project=${COVERITY_PROJ} https://scan.coverity.com/builds?project=${COVERITY_PROJ}
- name: Upload build.log - name: Upload build.log
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v4
with: with:
name: coverity-build.log name: coverity-build.log
path: cov-int/build-log.txt path: cov-int/build-log.txt
+12 -37
View File
@@ -7,38 +7,11 @@ on:
jobs: jobs:
release: release:
name: Create GitHub release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
release_id: ${{ steps.create_release.outputs.id }}
steps:
- uses: actions/checkout@v2
- name: Extract ChangeLog entry ...
# Hack to extract latest entry for body_path below
run: |
awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
|head -n -1 > release.md
cat release.md
- name: Create release ...
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Libite (-lite) ${{ github.ref }}
body_path: release.md
draft: false
prerelease: false
tarball:
name: Build and upload release tarball name: Build and upload release tarball
needs: release
if: startsWith(github.ref, 'refs/tags/') if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Installing dependencies ... - name: Installing dependencies ...
run: | run: |
sudo apt-get -y update sudo apt-get -y update
@@ -46,17 +19,19 @@ jobs:
- name: Creating Makefiles ... - name: Creating Makefiles ...
run: | run: |
./autogen.sh ./autogen.sh
./configure ./configure --prefix=
- name: Build release ... - name: Build release ...
run: | run: |
make release || (cat test/test-suite.log; false) make release
ls -lF ../
mkdir -p artifacts/ mkdir -p artifacts/
mv ../*.tar.* artifacts/ mv ../*.tar.* artifacts/
- name: Upload release artifacts ... - name: Extract ChangeLog entry ...
uses: skx/github-action-publish-binaries@release-0.15 run: |
env: awk '/-----*/{if (x == 1) exit; x=1;next}x' ChangeLog.md \
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |head -n -1 > release.md
cat release.md
- uses: ncipollo/release-action@v1
with: with:
releaseId: ${{ needs.release.outputs.release_id }} name: Libite (-lite) ${{ github.ref_name }}
args: artifacts/* bodyFile: "release.md"
artifacts: "artifacts/*"
+83 -1
View File
@@ -4,6 +4,82 @@ Change Log
All notable changes to the project are documented in this file. All notable changes to the project are documented in this file.
[UNRELEASED][]
--------------
### Fixes
- Fix `copyfile()` not preserving the source file mode when the process
umask is more restrictive
- Fix `mkpath()`, and `fmkpath()`, not applying the given mode when the
process umask is more restrictive, e.g., requesting 0755 with umask
0027 silently created a 0750 directory
[v2.6.2][] - 2025-11-22
-----------------------
### Fixes
- Fix memory leak in `which()` on `realloc()` failure
- Fix `pidfile()` to handle missing trailing slash in prefix path
[v2.6.1][] - 2023-10-15
-----------------------
### Fixes
- FTBFS uClibc and musl libc, `lite.h` missing `stdarg.h` include
[v2.6.0][] - 2023-09-17
-----------------------
### Changes
- New APIs from the [Infix Project](https://github.com/kernelkit/infix):
- `fexistf()`
- `vfopenf()`
- `popenf()`
- `vreadsnf()`, `readsnf()`, `writesf()`
- `vreadllf()`, `readllf()`, `readdf()`
- `writellf()`, `writedf()`
### Fixes
- Spellchecking of API docs
[v2.5.3][] - 2023-04-08
-----------------------
### Changes
- Refactor `copyfile()` unit test and verify number of bytes copied
- Add missing `rsync()` unit test to ensure proper function
### Fixes
- `rsync()` does not copy single files correctly
- `copyfile()` does not handle empty source files correctly (error)
- `copyfile()` does not properly create destination dir
[v2.5.2][] - 2022-04-17
-----------------------
### Changes
- Add printf format attribute hint to all vararg functions. This will
help users of these functions by telling CC what to look for
### Fixes
- Fix `fisdir()` segfault on GNU/Hurd
- Fix `touch)` on Debian/kFreeBSD, use `creat()` instad of `mknod()`
[v2.5.1][] - 2021-12-02
-----------------------
Debian-only fix.
### Fixes
- Use `dh_link(1)` to install `/usr/include/lite -> libite/`. The
symlink created by libite installer itself is not kept.
[v2.5.0][] - 2021-11-30 [v2.5.0][] - 2021-11-30
----------------------- -----------------------
@@ -453,7 +529,13 @@ problems this may cause!
Initial extraction of frog DNA from [Finit][]. See [README][] for API details. Initial extraction of frog DNA from [Finit][]. See [README][] for API details.
[UNRELEASED]: https://github.com/troglobit/libite/compare/v2.4.1...HEAD [UNRELEASED]: https://github.com/troglobit/libite/compare/v2.6.2...HEAD
[v2.6.2]: https://github.com/troglobit/libite/compare/v2.6.1...v2.6.2
[v2.6.1]: https://github.com/troglobit/libite/compare/v2.6.0...v2.6.1
[v2.6.0]: https://github.com/troglobit/libite/compare/v2.5.3...v2.6.0
[v2.5.3]: https://github.com/troglobit/libite/compare/v2.5.2...v2.5.3
[v2.5.2]: https://github.com/troglobit/libite/compare/v2.5.1...v2.5.2
[v2.5.1]: https://github.com/troglobit/libite/compare/v2.5.0...v2.5.1
[v2.5.0]: https://github.com/troglobit/libite/compare/v2.4.1...v2.5.0 [v2.5.0]: https://github.com/troglobit/libite/compare/v2.4.1...v2.5.0
[v2.4.1]: https://github.com/troglobit/libite/compare/v2.4.0...v2.4.1 [v2.4.1]: https://github.com/troglobit/libite/compare/v2.4.0...v2.4.1
[v2.4.0]: https://github.com/troglobit/libite/compare/v2.3.1...v2.4.0 [v2.4.0]: https://github.com/troglobit/libite/compare/v2.3.1...v2.4.0
+1 -1
View File
@@ -1,5 +1,5 @@
Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com> Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+15
View File
@@ -0,0 +1,15 @@
* TODO Write tests for new APIs
* More formatted variants?
- symlinkf()
- accessf()
- statf() / lstatf()
* Relocate from Finit
- fnread()
- fnwrite()
- fngetint()
- paste()
- unquote()
+1 -1
View File
@@ -1,4 +1,4 @@
AC_INIT(libite, 2.5.0, https://github.com/troglobit/libite/issues) AC_INIT(libite, 2.6.2, https://github.com/troglobit/libite/issues)
AC_CONFIG_AUX_DIR(aux) AC_CONFIG_AUX_DIR(aux)
AM_INIT_AUTOMAKE([1.11 foreign dist-xz]) AM_INIT_AUTOMAKE([1.11 foreign dist-xz])
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
+30
View File
@@ -1,3 +1,33 @@
libite (2.6.0) stable; urgency=medium
* New APIs from Infix Project:
- fexistf()
- vfopenf()
- popenf()
- vreadsnf(), readsnf(), writesf()
- vreadllf(), readllf(), readdf()
- writellf(), writedf()
* Spellchecking of API docs
-- Joachim Wiberg <troglobit@gmail.com> Sun, 17 Sep 2023 12:16:47 +0200
libite (2.5.3) stable; urgency=medium
* rsync() does not copy single files correctly
* copyfile() does not handle empty source files correctly (error)
* copyfile() does not properly create destination dir
* Add printf format attribute hint to all vararg functions
* Fix fisdir() segfault on GNU/Hurd
* Fix touch() on Debian/kFreeBSD, use creat() instad of mknod()
-- Joachim Wiberg <troglobit@gmail.com> Sat, 08 Apr 2023 07:04:00 +0200
libite (2.5.1) stable; urgency=medium
* Use dh_link(1) to install `/usr/include/lite -> libite/`.
-- Joachim Wiberg <troglobit@gmail.com> Thu, 02 Dec 2021 00:08:41 +0100
libite (2.5.0) stable; urgency=medium libite (2.5.0) stable; urgency=medium
* Header files have moved from `lite/*.h` to `libite/*.h` to prevent * Header files have moved from `lite/*.h` to `libite/*.h` to prevent
-1
View File
@@ -1,5 +1,4 @@
usr/include/libite/*.h usr/include/libite/*.h
usr/include/lite
usr/lib/*/lib*.a usr/lib/*/lib*.a
usr/lib/*/lib*.so usr/lib/*/lib*.so
usr/lib/*/pkgconfig/lib*.pc usr/lib/*/pkgconfig/lib*.pc
+1
View File
@@ -0,0 +1 @@
/usr/include/libite /usr/include/lite
+3
View File
@@ -6,6 +6,9 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
%: %:
dh $@ --with autoreconf dh $@ --with autoreconf
override_dh_auto_configure:
dh_auto_configure -- --without-symlink
override_dh_installchangelogs: override_dh_installchangelogs:
dh_installchangelogs ChangeLog.md dh_installchangelogs ChangeLog.md
+84 -1
View File
@@ -124,7 +124,7 @@ Generic Functions
Check for the existence of a directory, returns True(1) or False(0). Check for the existence of a directory, returns True(1) or False(0).
- `fopenf(mode, fmt, ...)` - `fopenf(mode, fmt, ...)` and `vfopenf(mode, fmt, ap)`
Like `fopen()`, but takes a formatted string as argument. This Like `fopen()`, but takes a formatted string as argument. This
greatly simplifies operations that usually consist of composing a greatly simplifies operations that usually consist of composing a
@@ -249,6 +249,19 @@ Generic Functions
`delete`, it is now called `opt`. The APIs are 100% compatible if the `delete`, it is now called `opt`. The APIs are 100% compatible if the
value `1` was used. value `1` was used.
- `runbg(cmd, delay)`
Run a command in the background, after delay microseconds, essentially
a background `execvp()` with a delay. The function employs double-fork
to prevent zombies and closes all open files, including stdio.
```C
char *cmd[] = { "sh", "-c", "echo 'hej' >/tmp/foo", NULL};
runbg(cmd, 200000); /* Create /tmp/foo after 200 millisec */
/* Execution continues here immediately */
```
- `strmatch(str, list)`, `strnmatch(str, list, len)` - `strmatch(str, list)`, `strnmatch(str, list, len)`
Find matching string in an array of strings. Returns index in array on Find matching string in an array of strings. Returns index in array on
@@ -307,6 +320,76 @@ Generic Functions
yes (both `y` and `Y` are handled) and `FALSE` for everything else. yes (both `y` and `Y` are handled) and `FALSE` for everything else.
Procfile Functions
------------------
These functions can be used to simplify access to data in `/proc` and
`/sys` on a Linux system.
### String Functions
- `readsnf(line, len, fmt, ...)`
Read first line from a file composed from `fmt`, with optional args.
At most `len` bytes from the file are stored in `line`.
On success this function returns `line` with any trailing newlines
`chomp()`ed off. On error `NULL` is returned.
- `vreadsnf(line, len, fmt, ap)`
Similar to `readsnf()` but takes a `va_list` argument instead.
- `writesf(str, mode, fmt, ...)`
Write the string buffer `str`, with an additional newline, to a file
composed from `fmt`, with optional args. The file is opened with the
given `mode`, e.g. "w+", which allows for both replacing and appending
file content.
The function returns POSIX OK(0) on success or -1 on failure with the
`errno` variable set to a value from either `fopen()` or `fclose()`.
### Integer Functions
Read and write 32-bit and 64-bit signed values from/to files.
- `vreadllf(value, fmt, ap)` and `readllf(value, fmt, ...)`
Only the first line is read. If the line has newline that is stripped
before calling `strtoll()` on the line. The arguments to the latter
function is such that it can convert also from a hexadecimal value.
This function may fail and return -1 on opening the file, reading a
line, or if the `strtoll()` function fails. All of which set the
`errno` variable. Otherwise this function return POSIX OK(0).
- `readdf(value, fmt, ...)`
Read a signed 32-bit value from a file composed from `fmt`, with
optional args. This function use `vreadllf()` with an additional
range checking for `INT_MIN` and `INT_MAX`. Values outside that
cause this function to return -1 with `errno` set to `ERANGE`.
- `writedf(value, mode, fmt, ...)`
Write a signed 32-bit `value` to a file composed from `fmt`, with
optional args. The file is opened with the given `mode`, e.g. "w+",
which allows for both replacing and appending file content.
The function returns POSIX OK(0) on success or -1 on failure with the
`errno` variable set to a value from either `fopen()` or `fclose()`.
- `writellf(value, mode, fmt, ...)`
Write a signed 64-bit `value` to a file composed from `fmt`, with
optional args. The file is opened with the given `mode`, e.g. "w+",
which allows for both replacing and appending file content.
The function returns POSIX OK(0) on success or -1 on failure with the
`errno` variable set to a value from either `fopen()` or `fclose()`.
GNU Functions GNU Functions
------------- -------------
+3 -3
View File
@@ -21,7 +21,7 @@
# The DX_*_FEATURE macros control the default setting for the given # The DX_*_FEATURE macros control the default setting for the given
# Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for # Doxygen feature. Supported features are 'DOXYGEN' itself, 'DOT' for
# generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML # generating graphics, 'HTML' for plain HTML, 'CHM' for compressed HTML
# help (for MS users), 'CHI' for generating a seperate .chi file by the # help (for MS users), 'CHI' for generating a separate .chi file by the
# .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate # .chm file, and 'MAN', 'RTF', 'XML', 'PDF' and 'PS' for the appropriate
# output formats. The environment variable DOXYGEN_PAPER_SIZE may be # output formats. The environment variable DOXYGEN_PAPER_SIZE may be
# specified to override the default 'a4wide' paper size. # specified to override the default 'a4wide' paper size.
@@ -321,8 +321,8 @@ DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation],
DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], DX_ENV_APPEND(GENERATE_HTMLHELP, YES)],
[DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)])
# Seperate CHI file generation. # Separate CHI file generation.
DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], DX_ARG_ABLE(chi, [generate doxygen separate compressed HTML help index file],
[DX_CHECK_DEPEND(chm, 1)], [DX_CHECK_DEPEND(chm, 1)],
[DX_CLEAR_DEPEND(chm, 1)], [DX_CLEAR_DEPEND(chm, 1)],
[], [],
+6 -6
View File
@@ -2,21 +2,21 @@ lib_LTLIBRARIES = libite.la
libite_la_CPPFLAGS = -D_GNU_SOURCE libite_la_CPPFLAGS = -D_GNU_SOURCE
libite_la_CFLAGS = -W -Wall -Wextra libite_la_CFLAGS = -W -Wall -Wextra
libite_la_LDFLAGS = $(AM_LDFLAGS) -version-info 8:1:3 libite_la_LDFLAGS = $(AM_LDFLAGS) -version-info 9:0:4
libite_la_SOURCES = chomp.c copyfile.c \ libite_la_SOURCES = chomp.c copyfile.c \
conio.c conio.h dir.c \ conio.c conio.h dir.c \
erasef.c fopenf.c fremove.c \ erasef.c fopenf.c fremove.c \
fexist.c fisdir.c \ fexist.c fisdir.c \
fparseln.c fsendfile.c \ fparseln.c fsendfile.c \
ifconfig.c lfile.c \ ifconfig.c lfile.c \
makepath.c progress.c \ makepath.c procval.c progress.c \
pidfile.c pidfilefn.c \ pidfile.c pidfilefn.c popenf.c \
reallocarray.c rsync.c strtrim.c \ reallocarray.c rsync.c runbg.c \
strlcpy.c strlcat.c strtonum.c \ strlcpy.c strlcat.c strtonum.c \
strdupa.h strndupa.h strnlen.h \ strdupa.h strndupa.h strnlen.h \
strmatch.c systemf.c touchf.c \ strmatch.c systemf.c strtrim.c \
telnet.c tempfile.c truncatef.c \ telnet.c tempfile.c truncatef.c \
yorn.c which.c \ touchf.c yorn.c which.c \
lite.h strlite.h \ lite.h strlite.h \
queue.h tree.h queue.h tree.h
+28 -5
View File
@@ -38,6 +38,7 @@ static int adjust_target(const char *src, char **dst)
{ {
int isdir = 0; int isdir = 0;
retry:
if (fisdir(*dst)) { if (fisdir(*dst)) {
int slash = 0; int slash = 0;
char *tmp, *ptr = strrchr(src, '/'); char *tmp, *ptr = strrchr(src, '/');
@@ -58,16 +59,25 @@ static int adjust_target(const char *src, char **dst)
sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr); sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
*dst = tmp; *dst = tmp;
} else {
struct stat st;
if (stat(*dst, &st) && fisslashdir(*dst)) {
makedir(*dst, 0755);
goto retry;
}
} }
return isdir; return isdir;
} }
/* Actual copy loop, used by both copyfile() and fcopyfile() /*
* breaks loop on error and EOF */ * Actual copy loop, used by both copyfile() and fcopyfile() breaks loop
* on error and EOF
*/
static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len) static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len)
{ {
ssize_t ret = 0, size = 0; ssize_t ret = 0, size = -1;
do { do {
size_t count = num > len ? len : num; size_t count = num > len ? len : num;
@@ -76,11 +86,16 @@ static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len)
if (ret <= 0) { if (ret <= 0) {
if (ret == -1 && EINTR == errno) if (ret == -1 && EINTR == errno)
continue; continue;
if (ret == 0 && size == -1)
size = 0;
break; break;
} }
if (ret > 0) if (ret > 0) {
if (size == -1)
size = 0;
size += write(out, buffer, ret); size += write(out, buffer, ret);
}
num -= count; num -= count;
} while (num > 0); } while (num > 0);
@@ -193,12 +208,20 @@ ssize_t copyfile(const char *src, const char *dst, int len, int opt)
goto exit; goto exit;
} }
/* open(2) applies umask, we want the source file mode */
if (fchmod(out, st.st_mode & 07777)) {
saved_errno = errno;
size = -1;
goto close_exit;
}
size = do_copy(in, out, num, buffer, BUFSIZ); size = do_copy(in, out, num, buffer, BUFSIZ);
if (!size && errno) if (size < 0)
saved_errno = errno; saved_errno = errno;
else if (keep_mtim) else if (keep_mtim)
set_mtime(in, out); set_mtime(in, out);
close_exit:
close(out); close(out);
close(in); close(in);
+38
View File
@@ -29,8 +29,13 @@
*/ */
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include "lite.h"
/** /**
* Check if a file exists in the file system. * Check if a file exists in the file system.
* @param file File to look for, with full path. * @param file File to look for, with full path.
@@ -50,6 +55,39 @@ int fexist(const char *file)
return 1; return 1;
} }
/**
* Like fexist() but with formatted string support
* @param fmt Formatted string to be composed into the file to look for.
*
* This is a wrapper for the fexist() function in lite.h, lessening the
* burden of having to compose the filename from parts in a seprate
* buffer.
*
* @returns @c TRUE(1) if the @a file exists, otherwise @c FALSE(0).
*/
int fexistf(const char *fmt, ...)
{
va_list ap;
char *file;
int len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
file = alloca(len + 1);
if (!file) {
errno = ENOMEM;
return -1;
}
va_start(ap, fmt);
vsnprintf(file, len + 1, fmt, ap);
va_end(ap);
return fexist(file);
}
/** /**
* Local Variables: * Local Variables:
* indent-tabs-mode: t * indent-tabs-mode: t
+1 -1
View File
@@ -41,7 +41,7 @@ int fisdir(const char *path)
{ {
struct stat sb; struct stat sb;
if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) if (path && !stat(path, &sb) && S_ISDIR(sb.st_mode))
return 1; return 1;
return 0; return 0;
+39 -15
View File
@@ -27,10 +27,43 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
/**
* Similar to fopenf() except it takes a @a va_list argument
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
* @param ap List of variable arguemnts from va_start().
*
* See fopenf() for details.
*
* @returns a FILE pointer, or @c NULL on error.
*/
FILE *vfopenf(const char *mode, const char *fmt, va_list ap)
{
va_list apc;
char *file;
int len;
va_copy(apc, ap);
len = vsnprintf(NULL, 0, fmt, apc);
va_end(apc);
file = alloca(len + 1);
if (!file) {
errno = ENOMEM;
return NULL;
}
va_copy(apc, ap);
vsnprintf(file, len + 1, fmt, apc);
va_end(apc);
return fopen(file, mode);
}
/** /**
* Open a file based on the formatted string and optional arguments * Open a file based on the formatted string and optional arguments
* @param mode An fopen() mode string, e.g. "w+" * @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname * @param fmt Formatted string to be composed into a pathname.
* *
* This function is an extension to the fopen() family, lessening the burden * This function is an extension to the fopen() family, lessening the burden
* of first having to compose the filename from parts in a seprate buffer. * of first having to compose the filename from parts in a seprate buffer.
@@ -42,24 +75,15 @@
FILE *fopenf(const char *mode, const char *fmt, ...) FILE *fopenf(const char *mode, const char *fmt, ...)
{ {
va_list ap; va_list ap;
char *file; FILE *fp;
int len;
va_start(ap, fmt); va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap); fp = vfopenf(mode, fmt, ap);
va_end(ap); va_end(ap);
if (!fp)
file = alloca(len + 1);
if (!file) {
errno = ENOMEM;
return NULL; return NULL;
}
va_start(ap, fmt); return fp;
vsnprintf(file, len + 1, fmt, ap);
va_end(ap);
return fopen(file, mode);
} }
/** /**
+33 -12
View File
@@ -1,7 +1,7 @@
/* Collection of frog DNA /* Collection of frog DNA
* *
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com> * Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
* Copyright (c) 2008-2021 Joachim Wiberg <troglobit@gmail.com> * Copyright (c) 2008-2023 Joachim Wiberg <troglobit@gmail.com>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,7 +26,7 @@
* Collection of frog DNA * Collection of frog DNA
* @file lite.h * @file lite.h
* @author Claudio Matsuoka (2008-2010) * @author Claudio Matsuoka (2008-2010)
* @author Joachim Wiberg (2008-2021) * @author Joachim Wiberg (2008-2023)
* @copyright MIT License * @copyright MIT License
* *
* The latest version of this manual and the libite (-lite) software * The latest version of this manual and the libite (-lite) software
@@ -44,6 +44,7 @@ extern "C"
#include <errno.h> #include <errno.h>
#include <fcntl.h> /* Definition of AT_* constants for utimensat() */ #include <fcntl.h> /* Definition of AT_* constants for utimensat() */
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <stdint.h> /* uint8_t, uint16_t, uint32_t, INT32_MAX, etc. */ #include <stdint.h> /* uint8_t, uint16_t, uint32_t, INT32_MAX, etc. */
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@@ -72,21 +73,33 @@ typedef struct sdbuf sdbuf_t; /**< Opqaue context struct for telnet APIs */
char *chomp (char *str); char *chomp (char *str);
int erasef (const char *fmt, ...); int erasef (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
char *fparseln (FILE *, size_t *, size_t *, const char[3], int); char *fparseln (FILE *, size_t *, size_t *, const char[3], int);
int fexist (const char *file); int fexist (const char *file);
int fexistf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));;
int fisdir (const char *path); int fisdir (const char *path);
FILE *fopenf (const char *mode, const char *fmt, ...); FILE *vfopenf (const char *mode, const char *fmt, va_list ap);
int fremove (const char *fmt, ...); FILE *fopenf (const char *mode, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int fremove (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
FILE *tempfile (void); FILE *tempfile (void);
ssize_t copyfile (const char *src, const char *dst, int len, int opt); ssize_t copyfile (const char *src, const char *dst, int len, int opt);
int movefile (const char *src, const char *dst); int movefile (const char *src, const char *dst);
int fcopyfile (FILE *src, FILE *dst); int fcopyfile (FILE *src, FILE *dst);
ssize_t fsendfile (FILE *src, FILE *dst, size_t len); ssize_t fsendfile (FILE *src, FILE *dst, size_t len);
int truncatef (off_t length, const char *fmt, ...); int truncatef (off_t length, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
char *vreadsnf (char *line, size_t len, const char *fmt, va_list ap);
char *readsnf (char *line, size_t len, const char *fmt, ...);
int writesf (const char *str, const char *mode, const char *fmt, ...);
int vreadllf (long long *value, const char *fmt, va_list ap);
int readllf (long long *value, const char *fmt, ...);
int readdf (int *value, const char *fmt, ...);
int writellf (long long value, const char *mode, const char *fmt, ...);
int writedf (int value, const char *mode, const char *fmt, ...);
int ifconfig (const char *ifname, const char *addr, const char *mask, int up); int ifconfig (const char *ifname, const char *addr, const char *mask, int up);
@@ -97,13 +110,15 @@ char *lfgetkey (lfile_t *lf, const char *key);
int lfgetint (lfile_t *lf, const char *key); int lfgetint (lfile_t *lf, const char *key);
int fgetint (const char *file, const char *sep, const char *key); int fgetint (const char *file, const char *sep, const char *key);
int fmkpath (mode_t mode, const char *fmt, ...); int fmkpath (mode_t mode, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int mkpath (const char *dir, mode_t mode); int mkpath (const char *dir, mode_t mode);
int makepath (const char *dir); int makepath (const char *dir);
int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip); int dir (const char *dir, const char *type, int (*filter) (const char *file), char ***list, int strip);
int rsync (char *src, char *dst, int opt, int (*filter) (const char *file)); int rsync (char *src, char *dst, int opt, int (*filter) (const char *file));
FILE *popenf (const char *type, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
int pidfile (const char *basename); int pidfile (const char *basename);
int pidfile_signal(const char *pidfile, int signal); int pidfile_signal(const char *pidfile, int signal);
pid_t pidfile_read (const char *pidfile); pid_t pidfile_read (const char *pidfile);
@@ -114,16 +129,17 @@ void *reallocarray(void *optr, size_t nmemb, size_t size);
void progress (int percent, int max_width); void progress (int percent, int max_width);
void progress_simple(int percent); void progress_simple(int percent);
int systemf (const char *fmt, ...); int runbg (char *const cmd[], int delay);
int systemf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
sdbuf_t*telnet_open (int addr, short port); sdbuf_t*telnet_open (int addr, short port);
int telnet_close (sdbuf_t *ctx); int telnet_close (sdbuf_t *ctx);
int telnet_expect (sdbuf_t *ctx, char *script[], FILE *output); int telnet_expect (sdbuf_t *ctx, char *script[], FILE *output);
int telnet_session (int addr, short port, char *script[], FILE *output); int telnet_session (int addr, short port, char *script[], FILE *output);
int touchf (const char *fmt, ...); int touchf (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
int yorn (const char *fmt, ...); int yorn (const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
char *which (const char *cmd); char *which (const char *cmd);
int whichp (const char *cmd); int whichp (const char *cmd);
@@ -139,10 +155,15 @@ int whichp (const char *cmd);
*/ */
static inline int touch(const char *path) static inline int touch(const char *path)
{ {
int fd;
if (utimensat(AT_FDCWD, path, NULL, 0)) { if (utimensat(AT_FDCWD, path, NULL, 0)) {
if (errno == ENOENT) if (errno != ENOENT)
return mknod((path), S_IFREG|0644, 0);
return -1; return -1;
fd = creat(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
if (fd < 0)
return -1;
close(fd);
} }
return 0; return 0;
} }
+7 -1
View File
@@ -35,6 +35,9 @@
* @param dir Directory to created, relative or absolute * @param dir Directory to created, relative or absolute
* @param mode A &mode_t mode to create @a dir with * @param mode A &mode_t mode to create @a dir with
* *
* Directories are created with exactly @a mode, the process umask is
* not applied like it is for mkdir(2).
*
* @returns POSIX OK(0) on success, otherwise -1 with @a errno set. * @returns POSIX OK(0) on success, otherwise -1 with @a errno set.
*/ */
int mkpath(const char *dir, mode_t mode) int mkpath(const char *dir, mode_t mode)
@@ -51,7 +54,10 @@ int mkpath(const char *dir, mode_t mode)
mkpath(dirname(strdupa(dir)), mode); mkpath(dirname(strdupa(dir)), mode);
return mkdir(dir, mode); if (mkdir(dir, mode))
return -1;
return chmod(dir, mode);
} }
/** /**
+5 -1
View File
@@ -45,6 +45,7 @@
#include <paths.h> #include <paths.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <unistd.h> #include <unistd.h>
#ifndef pidfile #ifndef pidfile
@@ -93,7 +94,10 @@ int pidfile(const char *basename)
} }
if (basename[0] != '/') { if (basename[0] != '/') {
if (asprintf(&pidfile_path, "%s%s.pid", __pidfile_path, basename) == -1) size_t len = strlen(__pidfile_path);
int slash = __pidfile_path[len > 0 ? len - 1 : 0] != '/';
if (asprintf(&pidfile_path, "%s%s%s.pid", __pidfile_path, slash ? "/" : "", basename) == -1)
return (-1); return (-1);
} else { } else {
if (asprintf(&pidfile_path, "%s", basename) == -1) if (asprintf(&pidfile_path, "%s", basename) == -1)
+63
View File
@@ -0,0 +1,63 @@
/* Formatted popen()
*
* Copyright (c) 2023 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2023 Joachim Wiberg <troglobit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
/**
* Formatted pipe stream to or from a process
* @param type A popen() type, "r" or "w", can also include "e" for @c O_CLOEXEC.
* @param fmt Formatted string to be composed into a shell command line.
*
* This function is an extension to popen(), lessening the burden of
* first having to compose the filename from parts in a seprate buffer.
*
* @returns Upon successful completion, popenf() returns a FILE pointer.
* Otherwise, @c NULL is returned and @a errno is set to indicate the
* error.
*/
FILE *popenf(const char *type, const char *fmt, ...)
{
va_list ap;
char *cmd;
int len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
cmd = alloca(len + 1);
if (!cmd) {
errno = ENOMEM;
return NULL;
}
va_start(ap, fmt);
vsnprintf(cmd, len + 1, fmt, ap);
va_end(ap);
return popen(cmd, type);
}
+229
View File
@@ -0,0 +1,229 @@
/* Reading and writing values/strings to/from /proc and /sys style files
*
* Copyright (c) 2023 Tobias Waldekranz <tobias@waldekranz.com>
* Copyright (c) 2023 Joachim Wiberg <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file procval.c
* @author Joachim Wiberg
* @date 2023
* @copyright ISC License
*/
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include "lite.h"
/**
* Similar to readsnf() except it takes a @a va_list argument
* @param line Pointer to line buffer.
* @param len Size of line buffer.
* @param fmt Formatted string to be composed into a pathname.
* @param ap List of variable arguemnts from va_start().
*
* For details, see readsnf().
*
* @returns same as readsnf().
*/
char *vreadsnf(char *line, size_t len, const char *fmt, va_list ap)
{
va_list apc;
FILE *fp;
va_copy(apc, ap);
fp = vfopenf("r", fmt, apc);
va_end(apc);
if (!fp)
return NULL;
if (!fgets(line, len, fp)) {
fclose(fp);
return NULL;
}
fclose(fp);
return chomp(line);
}
/**
* Read first line from a file composed from fmt and optional args.
* @param line Pointer to line buffer.
* @param len Size of line buffer.
* @param fmt Formatted string to be composed into a pathname.
*
* @returns On success, this function returns the @p line read from the
* file, with any trailing '\n' chomp()ed out. On error, @c NULL.
*/
char *readsnf(char *line, size_t len, const char *fmt, ...)
{
va_list ap;
char *ln;
va_start(ap, fmt);
ln = vreadsnf(line, len, fmt, ap);
va_end(ap);
return ln;
}
/**
* Write a string buffer to a file composed from fmt and optional args.
* @param str Pointer to string buffer, may be multiple lines.
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
*
* @returns result of operation, with @a errno set on error.
*/
int writesf(const char *str, const char *mode, const char *fmt, ...)
{
va_list ap;
FILE *fp;
va_start(ap, fmt);
fp = vfopenf(mode, fmt, ap);
va_end(ap);
if (!fp)
return -1;
fprintf(fp, "%s\n", str);
return fclose(fp);
}
/**
* Same as readllf() except it takes a @a va_list argument.
*/
int vreadllf(long long *value, const char *fmt, va_list ap)
{
char line[0x100];
if (!vreadsnf(line, sizeof(line), fmt, ap))
return -1;
errno = 0;
*value = strtoll(line, NULL, 0);
return errno ? -1 : 0;
}
/**
* Read 64-bit integer value from a file composed from fmt and optional args.
* @param value Pointer to where to store read 64-bit integer value.
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
*
* @returns result of operation, with @a errno set on error.
*/
int readllf(long long *value, const char *fmt, ...)
{
va_list ap;
int rc;
va_start(ap, fmt);
rc = vreadllf(value, fmt, ap);
va_end(ap);
return rc;
}
/**
* Read integer value from a file composed from fmt and optional args.
* @param value Pointer to where to store read integer value.
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
*
* @returns result of operation, with @a errno set on error.
*/
int readdf(int *value, const char *fmt, ...)
{
long long tmp;
va_list ap;
int rc;
va_start(ap, fmt);
rc = vreadllf(&tmp, fmt, ap);
va_end(ap);
if (rc)
return rc;
if (tmp < INT_MIN || tmp > INT_MAX) {
errno = ERANGE;
return -1;
}
*value = tmp;
return 0;
}
/**
* Write 64-bit integer value to a file composed from fmt and optional args.
* @param value 64-bit integer value to write.
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
*
* @returns result of operation, with @a errno set on error.
*/
int writellf(long long value, const char *mode, const char *fmt, ...)
{
va_list ap;
FILE *fp;
va_start(ap, fmt);
fp = vfopenf(mode, fmt, ap);
va_end(ap);
if (!fp)
return -1;
fprintf(fp, "%lld\n", value);
return fclose(fp);
}
/**
* Write integer value to a file composed from fmt and optional args.
* @param value Integer value to write.
* @param mode An fopen() mode string, e.g. "w+".
* @param fmt Formatted string to be composed into a pathname.
*
* @returns result of operation, with @a errno set on error.
*/
int writedf(int value, const char *mode, const char *fmt, ...)
{
va_list ap;
FILE *fp;
va_start(ap, fmt);
fp = vfopenf(mode, fmt, ap);
va_end(ap);
if (!fp)
return -1;
fprintf(fp, "%d\n", value);
return fclose(fp);
}
/**
* Local Variables:
* indent-tabs-mode: t
* c-file-style: "linux"
* End:
*/
+5 -3
View File
@@ -77,7 +77,9 @@ int rsync(char *src, char *dst, int opt, int (*filter)(const char *file))
char **files; /* Array of file names. */ char **files; /* Array of file names. */
struct stat st; struct stat st;
if (!fisdir(dst)) errno = 0;
if (stat(dst, &st) && fisslashdir(dst))
makedir(dst, 0755); makedir(dst, 0755);
if (!fisdir(src)) { if (!fisdir(src)) {
@@ -85,9 +87,9 @@ int rsync(char *src, char *dst, int opt, int (*filter)(const char *file))
return 1; return 1;
if (copy(src, dst, keep_mtim)) if (copy(src, dst, keep_mtim))
result++; return 1;
return errno; return 0;
} }
/* Copy dir as well? */ /* Copy dir as well? */
+93
View File
@@ -0,0 +1,93 @@
/* Run a command in the background: fork() + usleep() + execvp()
*
* Copyright (c) 2023 Joachim Wiberg <troglobit@gmail.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
* @file runbg.c
* @author Joachim Wiberg
* @date 2023
* @copyright ISC License
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
/**
* Run command in the background
* @param cmd NULL terminated list of command and optional arguments
* @param delay Microseconds after fork() to wait before calling cmd
*
* This function forks to run the given command @p cmd in the background.
* To ensure there are no lingering zombies the function actuall forks
* twice, and also calls setsid(), before it calls usleep() with the @p
* delay argument. The command @p cmd is handed over to execvp().
*
* Since it runs in the background it is not possible to get the return
* code of the command.
*
* @returns on successful (first) fork(), this function returns POSIX
* OK(0), otherwise -1 and @a errno is set to indicate the error.
*/
int runbg(char *const cmd[], int delay)
{
int pid = fork();
int rc;
if (!pid) {
int fd, maxfd;
if (setsid() == -1)
_exit(errno);
/* reparent to init */
pid = fork();
if (pid == -1)
_exit(errno);
if (pid > 0)
_exit(0);
maxfd = sysconf(_SC_OPEN_MAX);
if (maxfd == -1)
maxfd = 8192;
for (fd = 0; fd < maxfd; fd++)
close(fd);
usleep(delay);
_exit(execvp(cmd[0], cmd));
}
if (pid == -1)
return -1;
if (waitpid(pid, &rc, 0) == -1)
return -1;
if (WIFEXITED(rc)) {
errno = WEXITSTATUS(rc);
if (errno)
rc = -1;
else
rc = 0;
} else if (WIFSIGNALED(rc)) {
errno = EINTR;
rc = -1;
} else
rc = -1;
return rc;
}
+8 -5
View File
@@ -53,8 +53,9 @@ static char *strip_args(char *path)
*/ */
char *which(const char *cmd) char *which(const char *cmd)
{ {
char *ptr, *env, *path = NULL;
size_t pathlen = 0; size_t pathlen = 0;
char *ptr, *tok, *env, *path = NULL; const char *tok;
if (!cmd) { if (!cmd) {
errno = EINVAL; errno = EINVAL;
@@ -69,9 +70,7 @@ char *which(const char *cmd)
if (!access(path, X_OK)) if (!access(path, X_OK))
return path; return path;
if (path)
free(path); free(path);
return NULL; return NULL;
} }
@@ -88,11 +87,15 @@ char *which(const char *cmd)
size_t len = strlen(tok) + strlen(cmd) + 2; size_t len = strlen(tok) + strlen(cmd) + 2;
if (pathlen < len) { if (pathlen < len) {
path = realloc(path, len); char *tmp = realloc(path, len);
if (!path) {
if (!tmp) {
free(path);
free(env); free(env);
return NULL; return NULL;
} }
path = tmp;
pathlen = len; pathlen = len;
} }
+1
View File
@@ -15,6 +15,7 @@
/printhdr /printhdr
/progress /progress
/rsync /rsync
/runbg
/str /str
/strmatch /strmatch
/systemf /systemf
+1
View File
@@ -30,6 +30,7 @@ TESTS += pidfile
TESTS += printhdr TESTS += printhdr
TESTS += progress TESTS += progress
TESTS += rsync TESTS += rsync
TESTS += runbg
TESTS += str TESTS += str
TESTS += strmatch TESTS += strmatch
TESTS += systemf TESTS += systemf
+136 -37
View File
@@ -2,54 +2,153 @@
#include <errno.h> #include <errno.h>
#include "check.h" #include "check.h"
int main(void) #define ERR(fmt, args...) { printf(fmt ": %s\n", ##args, strerror(errno)); rc = 1; }
{ #define ERRX(fmt, args...) { printf(fmt "\n", ##args); rc = 1; }
int i = 0;
char *files[] = { static char *files[] = {
"/etc/passwd", "/tmp/mypwd", "/tmp/mypwd", "/etc/passwd", "/tmp/mypwd", "/tmp/mypwd",
"/etc/passwd", "/tmp/", "/tmp/passwd", "/etc/passwd", "/tmp/", "/tmp/passwd",
"/etc/passwd", "/tmp", "/tmp/passwd", "/etc/passwd", "/tmp", "/tmp/passwd",
NULL NULL
}; };
FILE *src, *dst;
printf("=>Start testing fcopyfile()\n"); static int one_sz(const char *src, const char *dst, int sz)
while (files[i]) { {
printf("fcopyfile(%s, %s)\t", files[i], files[i + 1]); struct stat st;
src = fopen(files[i], "r"); ssize_t len;
dst = fopen(files[i + 1], "w"); int rc = 0;
if (fcopyfile(src, dst)) {
if (!fisdir(files[i + 1])) len = copyfile(src, dst, sz, 0);
err(1, "Failed fcopyfile(%s, %s)", files[i], files[i + 1]); if (!len) {
ERR("failed copying any bytes from %s", src);
} else {
if (stat(dst, &st))
ERR("copied but cannot find destination %s", dst);
if (st.st_size != sz)
ERR("file only %ld bytes, expected %d", st.st_size, sz);
if (len != st.st_size)
ERRX("copied expected data %d, but copyfile() says %ld", sz, len);
} }
if (src) return rc;
fclose(src);
if (dst)
fclose(dst);
if (fexist(files[i + 2]))
printf("OK => %s", files[i + 2]);
printf("\n");
erase(files[i + 2]);
i += 3;
} }
printf("\n=>Start testing copyfile()\n"); static int _sz()
i = 0; {
while (files[i]) { const int sz = 32768;
printf("copyfile(%s, %s)\t", files[i], files[i + 1]);
if (!copyfile(files[i], files[i + 1], 0, 0))
err(1, "Failed copyfile(%s, %s)", files[i], files[i + 1]);
if (fexist(files[i + 2])) return test(one_sz("/dev/zero", "/tmp/zeroes", sz), "copyfile(/dev/zero, %d)", sz);
printf("OK => %s", files[i + 2]);
printf("\n");
erase(files[i + 2]);
i += 3;
} }
return 0; static int fcopyfile_one(const char *src, const char *dst, const char *expected)
{
FILE *from, *to;
int rc;
from = fopen(src, "r");
to = fopen(dst, "w");
rc = fcopyfile(from, to);
if (from)
fclose(from);
if (to)
fclose(to);
if (rc) {
if (!src || !dst)
rc = 0; /* expected */
else if (fisdir(dst))
rc = 0; /* expected */
} else {
if (fexist(expected)) {
if (systemf("diff -q %s %s", src, expected))
rc = 1;
} else
ERR("cannot find %s", expected);
}
erase(expected);
return rc;
}
static int _fcopyfile()
{
int i, rc = 0;
for (i = 0; files[i]; i += 3) {
char *src = files[i];
char *dst = files[i + 1];
char *res = files[i + 2];
rc += test(fcopyfile_one(src, dst, res), "fcopyfile(%s, %s)", src, dst);
}
return rc;
}
/* only checks success, correct file size verified by _sz() */
static int copyfile_one(const char *src, const char *dst, const char *expected)
{
int rc;
rc = copyfile(src, dst, 0, 0);
if (!rc)
rc = 1;
else if (!fexist(expected))
rc = 1;
else
rc = 0;
erase(expected);
return rc;
}
static int _copyfile()
{
int i, rc = 0;
for (i = 0; files[i]; i += 3) {
char *src = files[i];
char *dst = files[i + 1];
char *res = files[i + 2];
rc += test(copyfile_one(src, dst, res), "copyfile(%s, %s)", src, dst);
}
return rc;
}
/* copyfile() must preserve source mode, even with a restrictive umask */
static int _mode(void)
{
const char *src = "/tmp/libite-mode-src", *dst = "/tmp/libite-mode-dst";
struct stat st;
mode_t old;
int rc = 1;
if (touch(src) || chmod(src, 0644))
return test(1, "copyfile() mode, prepare %s", src);
old = umask(0077);
if (copyfile(src, dst, 0, 0) >= 0 && !stat(dst, &st))
rc = (st.st_mode & 07777) != 0644;
umask(old);
erase(src);
erase(dst);
return test(rc, "copyfile() preserves mode 0644 with umask 0077");
}
int main(void)
{
return _fcopyfile() ||
_copyfile() ||
_sz() ||
_mode();
} }
+34 -2
View File
@@ -50,6 +50,34 @@ int test_fmkpath(const char *subdir)
return ret; return ret;
} }
/* mkpath() must apply mode as given, not mode & ~umask */
int test_umask(void)
{
const char *dirs[] = { BASEDIR "/um", BASEDIR "/um/sub", NULL };
mode_t old = umask(0077);
struct stat sb;
int i, ret;
ret = mkpath(dirs[1], 0755);
umask(old);
if (ret) {
perror("Failed");
return ret;
}
for (i = 0; dirs[i]; i++) {
if (checkpath(dirs[i]) || stat(dirs[i], &sb))
return 1;
if ((sb.st_mode & 07777) != 0755) {
fprintf(stderr, "Failed: %s mode %04o, expected 0755\n",
dirs[i], sb.st_mode & 07777);
return 1;
}
}
return 0;
}
int main(void) int main(void)
{ {
int i, ret = 0; int i, ret = 0;
@@ -66,14 +94,18 @@ int main(void)
mkdir(BASEDIR, 0755); mkdir(BASEDIR, 0755);
printf("Pass 1/2: makepath() =========================\n"); printf("Pass 1/3: makepath() =========================\n");
for (i = 0; list[i] && !ret; i++) for (i = 0; list[i] && !ret; i++)
ret |= test_makepath(list[i]); ret |= test_makepath(list[i]);
printf("Pass 2/2: fmkpath() ==========================\n"); printf("Pass 2/3: fmkpath() ==========================\n");
for (i = 0; list[i] && !ret; i++) for (i = 0; list[i] && !ret; i++)
ret |= test_fmkpath(list[i] + strlen(BASEDIR) + 1); ret |= test_fmkpath(list[i] + strlen(BASEDIR) + 1);
printf("Pass 3/3: mkpath() with umask ================\n");
if (!ret)
ret |= test_umask();
printf("Done: Cleaning up ============================\n"); printf("Done: Cleaning up ============================\n");
system("rm -rf " BASEDIR); system("rm -rf " BASEDIR);
+1 -1
View File
@@ -41,7 +41,7 @@ static int mtime()
char buf[80]; char buf[80];
/* Must sleep a while here otherwise we execute too fast => no mtime change :-( */ /* Must sleep a while here otherwise we execute too fast => no mtime change :-( */
usleep(10000); sleep(2);
PRINT("Calling pidfile() again to update mtime ...\n"); PRINT("Calling pidfile() again to update mtime ...\n");
if (pidfile(pidfile_arg)) if (pidfile(pidfile_arg))
+65 -35
View File
@@ -48,52 +48,82 @@ void setup_test(void)
} }
} }
static void check_tree(char *heading, char *dir) int check_dir_to_dir(void)
{ {
if (verbose) { int rc = 0;
if (heading)
puts(heading); rc += rsync(SRC, DST, LITE_FOPT_KEEP_MTIME, NULL);
rc += systemf("diff -rq %s %s", SRC, DST);
systemf("rm -rf %s/*", DST);
return rc;
} }
// XXX: Fixme, add code to list tree, nftw() possibly int check_file_to_file(void)
{
int rc = 0;
touch(SRC "foo.txt");
PRINT("SRC ==========================\n");
if (verbose) system("ls -l " SRC);
rc += rsync(SRC "foo.txt", DST "foo.txt", LITE_FOPT_KEEP_MTIME, NULL);
if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt")) {
PRINT("No dst file or it's a directory\n");
rc++;
}
PRINT("DST ==========================\n");
if (verbose) system("ls -l " DST);
systemf("rm -rf %s %s/*", SRC "foo.txt" , DST);
return rc;
}
int check_file_to_dir(void)
{
int rc = 0;
touch(SRC "foo.txt");
PRINT("SRC ==========================\n");
if (verbose) system("ls -l " SRC);
rc += rsync(SRC "foo.txt", DST, LITE_FOPT_KEEP_MTIME, NULL);
if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt"))
rc++;
PRINT("DST ==========================\n");
if (verbose) system("ls -l " DST);
systemf("rm -rf %s %s/*", SRC "foo.txt" , DST);
return rc;
}
int check_file_to_nodir(void)
{
int rc = 0;
touch(SRC "foo.txt");
PRINT("SRC ==========================\n");
if (verbose) system("ls -l " SRC);
system("rm -rf " DST);
rc += rsync(SRC "foo.txt", DST, LITE_FOPT_KEEP_MTIME, NULL);
if (!fexist(DST "foo.txt") || fisdir(DST "foo.txt"))
rc++;
PRINT("DST ==========================\n");
if (verbose) system("ls -l " DST);
systemf("rm -rf %s %s/*", SRC "foo.txt" , DST);
return rc;
} }
int run_test(void) int run_test(void)
{ {
int result = 0; int result = 0;
#if 0
setup_test(); setup_test();
check_tree("Before:", BASE);
result += rsync(SRC, DST, 0, NULL); result += test(check_dir_to_dir(), "Verifying src/ -> dst/");
check_tree("After:", BASE); result += test(check_file_to_file(), "Verifying src/foo.txt -> dst/foo.txt");
cleanup_test(); result += test(check_file_to_dir(), "Verifying src/foo.txt -> dst/");
#endif result += test(check_file_to_nodir(),"Verifying src/foo.txt -> dst/ (non-existing target dir)");
setup_test();
sleep(1);
result += rsync(BASE "src", DST, LITE_FOPT_KEEP_MTIME, NULL);
check_tree("Only partial rsync of src <-- No slash!", BASE);
#if 0
cleanup_test();
setup_test();
result += rsync(BASE "src/sub1", BASE "dst", 0, NULL);
check_tree("Only partial rsync of src/sub1 <-- No slashes!!", BASE);
cleanup_test();
setup_test();
result += rsync(BASE "src/sub1/", DST, 0, NULL);
check_tree("Only partial rsync of src/sub1/", BASE);
cleanup_test();
setup_test();
result += rsync(BASE "src/sub1", DST, 0, NULL);
check_tree("Only partial rsync of src/sub1 <-- No slash!", BASE);
result += rsync("/etc", "/var/tmp", 0, NULL);
check_tree("Real life test:", "/var/tmp");
#endif
return result; return result;
} }
+23
View File
@@ -0,0 +1,23 @@
#include "check.h"
#define file "/tmp/runbg.txt"
int main(void)
{
char *cmd[] = {
"sh", "-c", "echo 'hello world' > " file, NULL
};
erase(file);
test(runbg(cmd, 100000), "Calling runbg");
if (test(fexist(file), "Verifying %s does not yet exist", file))
return 1;
test(sleep(1), "Waiting for runbg");
if (test(!fexist(file), "Verifying %s has been created", file))
return 1;
return 0;
}
+1 -1
View File
@@ -23,7 +23,7 @@ static int mtime(char *file)
stat(file, &before); stat(file, &before);
/* Must sleep a while here otherwise we execute too fast => no mtime change :-( */ /* Must sleep a while here otherwise we execute too fast => no mtime change :-( */
usleep(10000); sleep(2);
PRINT("Calling touch() again to update mtime ...\n"); PRINT("Calling touch() again to update mtime ...\n");
if (touch(file)) { if (touch(file)) {