Added PATH lookup for program argument in sink.

Fortune for sink's current commit: Half blessing − 半吉
master
Avril 3 years ago
parent e20df37d9b
commit 6eb20eeb46
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -1,4 +1,5 @@
# `sink` - sinks all input to /dev/null
# Usage: `sink [<program> [<args...>]]`
#
# Targets:
# `sink` (default): stripped `release` target. output: `sink`

@ -1,6 +1,16 @@
# `sink` - sinks all input into `/dev/null`
Re-routes `stdin`, `stdout` (and optionally, `stderr`) to `/dev/null`.
Re-routes `stdin`, `stdout` (and optionally, `stderr`) of a program to `/dev/null`.
## Usage
Command usage is in the form of: `./sink [<program name> [<arguments...>]]`.
Example:
```shell
$ sink /usr/bin/cat sink.c # Identical to `>>/dev/null 2>&1 cat sink.c`
```
When invoked with `<program name>`: `execve()`s into the program (if it exists or is found in `$PATH`) with `<arguments....>` and a passed-through `envp` (see below in *Compiler flags* on changing this behaviour.)
## Building
@ -10,6 +20,8 @@ To build for debugging, run `make debug`.
### Compiler flags
`-DREPLACE_STDERR` - Add to `CFLAGS` when building a target to re-route the program's `stderr` stream to the sink as well. By default, it is closed after the other streams have been successfully rewritten.
`-DNO_SEARCH_PATH` - Add to `CFLAGS` when building to prevent the program looking up its argument in the `PATH` environment variable.
`-DNO_ENV` - Add to `CFLAGS` when building to prevent `envp` passthrough to the `execve()`'d program.
### Installation
The program must have been built before installation, and installation and uninstallation must be done as root.

103
sink.c

@ -1,6 +1,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>
#define r_stdin 0
#define r_stdout 1
@ -22,25 +26,112 @@ static inline int dupall(int from)
#ifdef REPLACE_STDERR
if(UNLIKELY(dup2(from, r_stderr) < 0)) {
perror("failed to dup2() stderr to sink");
#else
/*#else
if(UNLIKELY(close(r_stderr) != 0)) {
perror("failed to close stderr");
#endif
*/
return 3;
}
#endif
return 0;
}
int main(void)
inline static int err_not_found(int er) {
return er == ENOENT;
}
static inline int path_exists(const char fname[restrict static 1])
{
return access(fname, F_OK | X_OK) != -1;
}
static int path_lookup(size_t sz; const char name[restrict static 1], char fullpath[restrict sz], size_t sz, const char envpath[restrict static 1])
{
char* paths = strdup(envpath);
char* _tmpfree = paths;
int found = 0;
const char* item;
while((item = strsep(&paths, ":")) != NULL) {
if(UNLIKELY( ((size_t)snprintf(fullpath, sz, "%s/%s", item, name)) >= sz )) {
#if defined(DEBUG) && !defined(REPLACE_STDERR)
fprintf(stderr, "Warning: normalised path item '%s' truncated (from %s/%s). Full path was longer than %lu bytes\n", fullpath, item, name, sz);
#endif
errno = ENAMETOOLONG;
continue;
}
if(path_exists(fullpath)) {
found = 1;
break;
}
}
free(_tmpfree);
return found;
}
int main(int argc, char** argv, char** envp)
{
(void)argc;
#ifdef NO_ENV
#define GET_ENV ((char*[]){})
(void)envp;
#else
#define GET_ENV envp
#endif
#define $execve(path) execve((path), argv+1, GET_ENV)
int null = open("/dev/null", O_RDWR);
if(UNLIKELY(null<0)) {
perror("failed to open /dev/null");
return 1;
}
register int rc = dupall(null);
if(LIKELY(rc)) close(null);
return rc;
if(LIKELY(!rc)) close(null);
else return rc;
#ifdef REPLACE_STDERR
#define perror(v) ((void)(v))
#define eprintf(...) ((void)(__VA_ARGS__))
#else
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
#endif
if(argv[1]) {
#if NO_SEARCH_PATH
if(UNLIKELY($execve(argv[1]) < 0)) {
perror("execve() failed");
return errno;
}
#else
#define $try_execve(path) do { if(UNLIKELY($execve((path)) < 0)) { perror("execve() failed"); return errno; } else __builtin_unreachable(); } while(0)
const char* path = NULL;
int err = 0;
if(LIKELY(($execve(argv[1]) < 0)
&& (err_not_found(err = errno))
&& (path = getenv("PATH")))) {
// Failed to exec raw pathname (not found), lookup in $PATH
char fullpath[PATH_MAX+1];
errno = 0;
if(path_lookup(argv[1], fullpath, PATH_MAX, path)) $try_execve(fullpath);
else {
eprintf("Error: failed to find %s in PATH\n", argv[1]);
return errno ?: -1;
}
} else {
if(UNLIKELY(!err)) err = errno;
if(err_not_found(err)) {
perror(path
? "execve() failed. Program not found in PATH"
: "execve() failed. No PATH set to look through");
} else perror("execve() failed");
return err ?: -1;
}
#endif
}
return 0;
}

Loading…
Cancel
Save