summaryrefslogtreecommitdiffstats
path: root/src/ctx.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ctx.c')
-rw-r--r--src/ctx.c174
1 files changed, 93 insertions, 81 deletions
diff --git a/src/ctx.c b/src/ctx.c
index a940bed..05baa1d 100644
--- a/src/ctx.c
+++ b/src/ctx.c
@@ -2,45 +2,27 @@
// SPDX-License-Identifier: 0BSD
#include "ctx.h"
+
#include "alloc.h"
+#include "bfstd.h"
#include "color.h"
-#include "darray.h"
#include "diag.h"
#include "expr.h"
+#include "list.h"
#include "mtab.h"
#include "pwcache.h"
+#include "sighook.h"
#include "stat.h"
#include "trie.h"
-#include "xtime.h"
+
#include <errno.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
-
-const char *debug_flag_name(enum debug_flags flag) {
- switch (flag) {
- case DEBUG_COST:
- return "cost";
- case DEBUG_EXEC:
- return "exec";
- case DEBUG_OPT:
- return "opt";
- case DEBUG_RATES:
- return "rates";
- case DEBUG_SEARCH:
- return "search";
- case DEBUG_STAT:
- return "stat";
- case DEBUG_TREE:
- return "tree";
-
- case DEBUG_ALL:
- break;
- }
-
- bfs_bug("Unrecognized debug flag");
- return "???";
-}
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
struct bfs_ctx *bfs_ctx_new(void) {
struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx);
@@ -48,19 +30,30 @@ struct bfs_ctx *bfs_ctx_new(void) {
return NULL;
}
+ SLIST_INIT(&ctx->expr_list);
+ ARENA_INIT(&ctx->expr_arena, struct bfs_expr);
+
ctx->maxdepth = INT_MAX;
ctx->flags = BFTW_RECOVER;
ctx->strategy = BFTW_BFS;
ctx->optlevel = 3;
+ ctx->threads = nproc();
+ if (ctx->threads > 8) {
+ // Not much speedup after 8 threads
+ ctx->threads = 8;
+ }
+
trie_init(&ctx->files);
- struct rlimit rl;
- if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
+ ctx->umask = umask(0);
+ umask(ctx->umask);
+
+ if (getrlimit(RLIMIT_NOFILE, &ctx->orig_nofile) != 0) {
goto fail;
}
- ctx->nofile_soft = rl.rlim_cur;
- ctx->nofile_hard = rl.rlim_max;
+ ctx->cur_nofile = ctx->orig_nofile;
+ ctx->raise_nofile = true;
ctx->users = bfs_users_new();
if (!ctx->users) {
@@ -72,7 +65,7 @@ struct bfs_ctx *bfs_ctx_new(void) {
goto fail;
}
- if (xgettime(&ctx->now) != 0) {
+ if (clock_gettime(CLOCK_REALTIME, &ctx->now) != 0) {
goto fail;
}
@@ -106,13 +99,20 @@ struct bfs_ctx_file {
CFILE *cfile;
/** The path to the file (for diagnostics). */
const char *path;
+ /** Signal hook to send a reset escape sequence. */
+ struct sighook *hook;
/** Remembers I/O errors, to propagate them to the exit status. */
int error;
};
+/** Call cfreset() on a tracked file. */
+static void cfreset_hook(int sig, siginfo_t *info, void *arg) {
+ cfreset(arg);
+}
+
CFILE *bfs_ctx_dedup(struct bfs_ctx *ctx, CFILE *cfile, const char *path) {
struct bfs_stat sb;
- if (bfs_stat(fileno(cfile->file), NULL, 0, &sb) != 0) {
+ if (bfs_stat(cfile->fd, NULL, 0, &sb) != 0) {
return NULL;
}
@@ -132,19 +132,31 @@ CFILE *bfs_ctx_dedup(struct bfs_ctx *ctx, CFILE *cfile, const char *path) {
leaf->value = ctx_file = ALLOC(struct bfs_ctx_file);
if (!ctx_file) {
- trie_remove(&ctx->files, leaf);
- return NULL;
+ goto fail;
}
ctx_file->cfile = cfile;
ctx_file->path = path;
ctx_file->error = 0;
+ ctx_file->hook = NULL;
+
+ if (cfile->colors) {
+ ctx_file->hook = atsigexit(cfreset_hook, cfile);
+ if (!ctx_file->hook) {
+ goto fail;
+ }
+ }
if (cfile != ctx->cout && cfile != ctx->cerr) {
++ctx->nfiles;
}
return cfile;
+
+fail:
+ trie_remove(&ctx->files, leaf);
+ free(ctx_file);
+ return NULL;
}
void bfs_ctx_flush(const struct bfs_ctx *ctx) {
@@ -152,7 +164,7 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) {
// - the user sees everything relevant before an -ok[dir] prompt
// - output from commands is interleaved consistently with bfs
// - executed commands can rely on I/O from other bfs actions
- TRIE_FOR_EACH(&ctx->files, leaf) {
+ for_trie (leaf, &ctx->files) {
struct bfs_ctx_file *ctx_file = leaf->value;
CFILE *cfile = ctx_file->cfile;
if (fflush(cfile->file) == 0) {
@@ -164,11 +176,10 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) {
const char *path = ctx_file->path;
if (path) {
- bfs_error(ctx, "'%s': %m.\n", path);
+ bfs_error(ctx, "%pq: %s.\n", path, errstr());
} else if (cfile == ctx->cout) {
- bfs_error(ctx, "(standard output): %m.\n");
+ bfs_error(ctx, "(standard output): %s.\n", errstr());
}
-
}
// Flush the user/group caches, in case the executed command edits the
@@ -197,30 +208,47 @@ static int bfs_ctx_fflush(CFILE *cfile) {
static int bfs_ctx_fclose(struct bfs_ctx *ctx, struct bfs_ctx_file *ctx_file) {
CFILE *cfile = ctx_file->cfile;
- if (cfile == ctx->cout) {
- // Will be checked later
- return 0;
- } else if (cfile == ctx->cerr) {
- // Writes to stderr are allowed to fail silently, unless the same file was used by
- // -fprint, -fls, etc.
- if (ctx_file->path) {
- return bfs_ctx_fflush(cfile);
- } else {
- return 0;
- }
- }
-
+ // Writes to stderr are allowed to fail silently, unless the same file
+ // was used by -fprint, -fls, etc.
+ bool silent = cfile == ctx->cerr && !ctx_file->path;
int ret = 0, error = 0;
- if (ferror(cfile->file)) {
+
+ if (ctx_file->error) {
+ // An error was previously reported during bfs_ctx_flush()
ret = -1;
- error = EIO;
+ error = ctx_file->error;
}
- if (cfclose(cfile) != 0) {
+
+ // Flush the file just before we remove the hook, to maximize the chance
+ // we leave the TTY in a good state
+ if (bfs_ctx_fflush(cfile) != 0) {
ret = -1;
error = errno;
}
- errno = error;
+ sigunhook(ctx_file->hook);
+
+ // Close the CFILE, except for stdio streams, which are closed later
+ if (cfile != ctx->cout && cfile != ctx->cerr) {
+ if (cfclose(cfile) != 0) {
+ ret = -1;
+ error = errno;
+ }
+ }
+
+ if (silent) {
+ ret = 0;
+ }
+
+ if (ret != 0 && ctx->cerr) {
+ if (ctx_file->path) {
+ bfs_error(ctx, "%pq: %s.\n", ctx_file->path, xstrerror(error));
+ } else if (cfile == ctx->cout) {
+ bfs_error(ctx, "(standard output): %s.\n", xstrerror(error));
+ }
+ }
+
+ free(ctx_file);
return ret;
}
@@ -231,50 +259,34 @@ int bfs_ctx_free(struct bfs_ctx *ctx) {
CFILE *cout = ctx->cout;
CFILE *cerr = ctx->cerr;
- bfs_expr_free(ctx->exclude);
- bfs_expr_free(ctx->expr);
-
bfs_mtab_free(ctx->mtab);
bfs_groups_free(ctx->groups);
bfs_users_free(ctx->users);
- TRIE_FOR_EACH(&ctx->files, leaf) {
+ for_trie (leaf, &ctx->files) {
struct bfs_ctx_file *ctx_file = leaf->value;
-
- if (ctx_file->error) {
- // An error was previously reported during bfs_ctx_flush()
- ret = -1;
- }
-
if (bfs_ctx_fclose(ctx, ctx_file) != 0) {
- if (cerr) {
- bfs_error(ctx, "'%s': %m.\n", ctx_file->path);
- }
ret = -1;
}
-
- free(ctx_file);
}
trie_destroy(&ctx->files);
- if (cout && bfs_ctx_fflush(cout) != 0) {
- if (cerr) {
- bfs_error(ctx, "(standard output): %m.\n");
- }
- ret = -1;
- }
-
cfclose(cout);
cfclose(cerr);
-
free_colors(ctx->colors);
- for (size_t i = 0; i < darray_length(ctx->paths); ++i) {
+ for_slist (struct bfs_expr, expr, &ctx->expr_list, freelist) {
+ bfs_expr_clear(expr);
+ }
+ arena_destroy(&ctx->expr_arena);
+
+ for (size_t i = 0; i < ctx->npaths; ++i) {
free((char *)ctx->paths[i]);
}
- darray_free(ctx->paths);
+ free(ctx->paths);
+ free(ctx->kinds);
free(ctx->argv);
free(ctx);
}