mirror of
https://github.com/libfuse/libfuse.git
synced 2026-07-07 14:47:41 +08:00
Add a new daemonize API
Existing example/ file systems do the fuse_daemonize() after
fuse_session_mount() - i.e. after the mount point is already
established. Though, these example/ daemons do not start
extra threads and do not need network initialization either.
fuse_daemonize() also does not allow to return notification
from the forked child to the parent.
Complex fuse file system daemons often want the order of
1) fork - parent watches, child does the work
Child:
2) start extra threads and system initialization (like network
connection and RDMA memory registration) from the fork child.
3) Start the fuse session after everything else succeeded
Parent:
Report child initialization success or failure
A new API is introduced to overcome the limitations of
fuse_daemonize()
fuse_daemonize_start() - fork, but foreground process does not
terminate yet and watches the background.
fuse_daemonize_success() / fuse_daemonize_fail() - background
daemon signals to the foreground process success or failure.
fuse_daemonize_active() - helper function for the high level
interface, which needs to handle both APIs. fuse_daemonize()
is called within fuse_main(), which now needs to know if the caller
actually already used the new API itself.
The object 'struct fuse_daemonize *' is allocated dynamically
and stored a global variable in fuse_daemonize.c, because
- high level fuse_main_real_versioned() needs to know
if already daemonized
- high level daemons do not have access to struct fuse_session
- FUSE_SYNC_INIT in later commits can only be done if the new
API (or a file system internal) daemonization is used.
Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
This commit is contained in:
@@ -55,6 +55,7 @@
|
||||
#include <errno.h>
|
||||
#include <ftw.h>
|
||||
#include <fuse_lowlevel.h>
|
||||
#include <fuse_daemonize.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <sys/file.h>
|
||||
@@ -1580,6 +1581,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
struct fuse_loop_config *loop_config = NULL;
|
||||
void *teardown_watchog = NULL;
|
||||
unsigned int daemon_flags = 0;
|
||||
|
||||
// Parse command line options
|
||||
auto options{ parse_options(argc, argv) };
|
||||
@@ -1638,10 +1640,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
fuse_loop_cfg_set_clone_fd(loop_config, fs.clone_fd);
|
||||
|
||||
if (fuse_session_mount(se, argv[2]) != 0)
|
||||
/* Start daemonization before mount so parent can report mount failure */
|
||||
if (fs.foreground)
|
||||
daemon_flags |= FUSE_DAEMONIZE_NO_BACKGROUND;
|
||||
if (fuse_daemonize_early_start(daemon_flags) != 0)
|
||||
goto err_out3;
|
||||
|
||||
fuse_daemonize(fs.foreground);
|
||||
if (fuse_session_mount(se, argv[2]) != 0)
|
||||
goto err_out4;
|
||||
|
||||
if (!fs.foreground)
|
||||
fuse_log_enable_syslog("passthrough-hp", LOG_PID | LOG_CONS,
|
||||
@@ -1650,7 +1656,7 @@ int main(int argc, char *argv[])
|
||||
teardown_watchog = fuse_session_start_teardown_watchdog(
|
||||
se, fs.root.stop_timeout_secs, NULL, NULL);
|
||||
if (teardown_watchog == NULL)
|
||||
goto err_out3;
|
||||
goto err_out4;
|
||||
|
||||
if (options.count("single"))
|
||||
ret = fuse_session_loop(se);
|
||||
@@ -1659,6 +1665,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
fuse_session_unmount(se);
|
||||
|
||||
err_out4:
|
||||
if (fuse_daemonize_early_is_active())
|
||||
fuse_daemonize_early_fail(ret);
|
||||
err_out3:
|
||||
fuse_remove_signal_handlers(se);
|
||||
err_out2:
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2026 Bernd Schubert <bsbernd.com>
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU LGPLv2.
|
||||
* See the file COPYING.LIB.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FUSE_DAEMONIZE_H_
|
||||
#define FUSE_DAEMONIZE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Flags for fuse_daemonize_start()
|
||||
*/
|
||||
#define FUSE_DAEMONIZE_NO_CHDIR (1 << 0)
|
||||
#define FUSE_DAEMONIZE_NO_BACKGROUND (1 << 1)
|
||||
|
||||
/**
|
||||
* Start daemonization process.
|
||||
*
|
||||
* Unless FUSE_DAEMONIZE_NO_BACKGROUND is set, this forks the process.
|
||||
* The parent waits for a signal from the child via fuse_daemonize_success()
|
||||
* or fuse_daemonize_fail().
|
||||
* The child returns from this call and continues setup.
|
||||
*
|
||||
* Unless FUSE_DAEMONIZE_NO_CHDIR is set, changes directory to "/".
|
||||
*
|
||||
* Must be called before fuse_session_mount().
|
||||
*
|
||||
* @param flags combination of FUSE_DAEMONIZE_* flags
|
||||
* @return 0 on success, negative errno on error
|
||||
*/
|
||||
int fuse_daemonize_early_start(unsigned int flags);
|
||||
|
||||
/**
|
||||
* Signal daemonization failure to parent and cleanup.
|
||||
*
|
||||
* To be called from the child process on any kind of error.
|
||||
*
|
||||
* @param err Error code passed to the parent and used as process exit code.
|
||||
|
||||
*/
|
||||
void fuse_daemonize_early_fail(int err);
|
||||
|
||||
/**
|
||||
* Check if daemonization is active and waiting for signal.
|
||||
*
|
||||
* Can be called from the child process to check state of daemonization.
|
||||
*
|
||||
* @return true if active, false otherwise
|
||||
*/
|
||||
bool fuse_daemonize_early_is_active(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FUSE_DAEMONIZE_H_ */
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
libfuse_headers = [ 'fuse.h', 'fuse_common.h', 'fuse_lowlevel.h',
|
||||
'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h' ]
|
||||
'fuse_opt.h', 'cuse_lowlevel.h', 'fuse_log.h',
|
||||
'fuse_daemonize.h' ]
|
||||
|
||||
install_headers(libfuse_headers, subdir: 'fuse3')
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2026 Bernd Schubert <bsbernd.com>
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU LGPLv2.
|
||||
* See the file COPYING.LIB.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "fuse_daemonize.h"
|
||||
#include "fuse_daemonize_i.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <err.h>
|
||||
|
||||
/**
|
||||
* Status values for fuse_daemonize_success() and fuse_daemonize_fail()
|
||||
*/
|
||||
#define FUSE_DAEMONIZE_SUCCESS 0
|
||||
#define FUSE_DAEMONIZE_FAILURE 1
|
||||
|
||||
/* Private/internal data */
|
||||
struct fuse_daemonize {
|
||||
unsigned int flags;
|
||||
int signal_pipe_wr; /* write end for signaling parent */
|
||||
int death_pipe_rd; /* read end, POLLHUP when parent dies */
|
||||
int stop_pipe_rd; /* read end for stop signal */
|
||||
int stop_pipe_wr; /* write end for stop signal */
|
||||
pthread_t watcher;
|
||||
bool watcher_started;
|
||||
_Atomic bool active;
|
||||
_Atomic bool daemonized;
|
||||
_Atomic bool mounted;
|
||||
};
|
||||
|
||||
/* Global daemonization object pointer */
|
||||
static struct fuse_daemonize daemonize = {
|
||||
.signal_pipe_wr = -1,
|
||||
.death_pipe_rd = -1,
|
||||
.stop_pipe_rd = -1,
|
||||
.stop_pipe_wr = -1,
|
||||
};
|
||||
|
||||
/* Watcher thread: polls for parent death or stop signal */
|
||||
static void *parent_watcher_thread(void *arg)
|
||||
{
|
||||
struct fuse_daemonize *di = arg;
|
||||
struct pollfd pfd[2];
|
||||
|
||||
pfd[0].fd = di->death_pipe_rd;
|
||||
pfd[0].events = POLLIN;
|
||||
pfd[1].fd = di->stop_pipe_rd;
|
||||
pfd[1].events = POLLIN;
|
||||
|
||||
while (1) {
|
||||
int rc = poll(pfd, 2, -1);
|
||||
|
||||
if (rc < 0)
|
||||
continue;
|
||||
|
||||
/* Parent died - death pipe write end closed */
|
||||
if (pfd[0].revents & (POLLHUP | POLLERR))
|
||||
_exit(EXIT_FAILURE);
|
||||
|
||||
/* Stop signal received */
|
||||
if (pfd[1].revents & POLLIN)
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int start_parent_watcher(struct fuse_daemonize *dm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = pthread_create(&dm->watcher, NULL, parent_watcher_thread,
|
||||
dm);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "fuse_daemonize: pthread_create: %s\n",
|
||||
strerror(rc));
|
||||
return -rc;
|
||||
}
|
||||
dm->watcher_started = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stop_parent_watcher(struct fuse_daemonize *dm)
|
||||
{
|
||||
char byte = 0;
|
||||
|
||||
if (dm && dm->watcher_started) {
|
||||
/* Signal watcher to stop */
|
||||
if (write(dm->stop_pipe_wr, &byte, 1) != 1)
|
||||
perror("fuse_daemonize: stop write");
|
||||
pthread_join(dm->watcher, NULL);
|
||||
dm->watcher_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int daemonize_child(struct fuse_daemonize *dm)
|
||||
{
|
||||
int stop_pipe[2], err = 0;
|
||||
|
||||
if (pipe(stop_pipe) == -1) {
|
||||
err = -errno;
|
||||
perror("fuse_daemonize_start: stop pipe");
|
||||
return err;
|
||||
}
|
||||
dm->stop_pipe_rd = stop_pipe[0];
|
||||
dm->stop_pipe_wr = stop_pipe[1];
|
||||
|
||||
if (setsid() == -1) {
|
||||
err = -errno;
|
||||
perror("fuse_daemonize_start: setsid");
|
||||
goto err_close_stop;
|
||||
}
|
||||
|
||||
/* Close stdin immediately */
|
||||
int nullfd = open("/dev/null", O_RDWR, 0);
|
||||
|
||||
if (nullfd != -1) {
|
||||
(void)dup2(nullfd, 0);
|
||||
if (nullfd > 0)
|
||||
close(nullfd);
|
||||
}
|
||||
|
||||
/* Start watcher thread to detect parent death */
|
||||
err = start_parent_watcher(dm);
|
||||
if (err)
|
||||
goto err_close_stop;
|
||||
|
||||
dm->daemonized = true;
|
||||
return 0;
|
||||
|
||||
err_close_stop:
|
||||
close(dm->stop_pipe_rd);
|
||||
close(dm->stop_pipe_wr);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Fork and daemonize. Returns 0 in child, never returns in parent. */
|
||||
static int do_daemonize(struct fuse_daemonize *dm)
|
||||
{
|
||||
int signal_pipe[2], death_pipe[2], err;
|
||||
|
||||
if (pipe(signal_pipe) == -1) {
|
||||
err = -errno;
|
||||
perror("fuse_daemonize_start: signal pipe");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (pipe(death_pipe) == -1) {
|
||||
err = -errno;
|
||||
perror("fuse_daemonize_start: death pipe");
|
||||
close(signal_pipe[0]);
|
||||
close(signal_pipe[1]);
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
err = -errno;
|
||||
perror("fuse_daemonize_start: fork");
|
||||
close(signal_pipe[0]);
|
||||
close(signal_pipe[1]);
|
||||
close(death_pipe[0]);
|
||||
close(death_pipe[1]);
|
||||
return err;
|
||||
|
||||
case 0:
|
||||
/* Child: signal write end, death read end */
|
||||
close(signal_pipe[0]);
|
||||
close(death_pipe[1]);
|
||||
dm->signal_pipe_wr = signal_pipe[1];
|
||||
dm->death_pipe_rd = death_pipe[0];
|
||||
return daemonize_child(dm);
|
||||
|
||||
default: {
|
||||
/* Parent: signal read end, death write end (kept open) */
|
||||
int status;
|
||||
ssize_t res;
|
||||
|
||||
close(signal_pipe[1]);
|
||||
close(death_pipe[0]);
|
||||
|
||||
res = read(signal_pipe[0], &status, sizeof(status));
|
||||
close(signal_pipe[0]);
|
||||
close(death_pipe[1]);
|
||||
|
||||
if (res != sizeof(status))
|
||||
_exit(EXIT_FAILURE);
|
||||
_exit(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int fuse_daemonize_early_start(unsigned int flags)
|
||||
{
|
||||
struct fuse_daemonize *dm = &daemonize;
|
||||
int err = 0;
|
||||
|
||||
dm->flags = flags;
|
||||
dm->signal_pipe_wr = -1;
|
||||
dm->death_pipe_rd = -1;
|
||||
dm->stop_pipe_rd = -1;
|
||||
dm->stop_pipe_wr = -1;
|
||||
dm->active = true;
|
||||
|
||||
if (!(flags & FUSE_DAEMONIZE_NO_CHDIR))
|
||||
(void)chdir("/");
|
||||
|
||||
if (!(flags & FUSE_DAEMONIZE_NO_BACKGROUND))
|
||||
err = do_daemonize(dm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void close_if_valid(int *fd)
|
||||
{
|
||||
if (*fd != -1) {
|
||||
close(*fd);
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void fuse_daemonize_early_signal(int status)
|
||||
{
|
||||
struct fuse_daemonize *dm = &daemonize;
|
||||
int rc;
|
||||
|
||||
if (!dm->active)
|
||||
errx(EINVAL, "%s: not active and cannot signal status", __func__);
|
||||
|
||||
/* Warn because there might be races */
|
||||
if (status == FUSE_DAEMONIZE_SUCCESS && !dm->mounted)
|
||||
fprintf(stderr, "fuse daemonize success without being mounted\n");
|
||||
|
||||
dm->active = false;
|
||||
|
||||
/* Stop watcher before signaling - parent will exit after this */
|
||||
stop_parent_watcher(dm);
|
||||
|
||||
/* Signal status to parent */
|
||||
if (dm->signal_pipe_wr != -1) {
|
||||
rc = write(dm->signal_pipe_wr, &status, sizeof(status));
|
||||
if (rc != sizeof(status))
|
||||
fprintf(stderr, "%s: write failed\n", __func__);
|
||||
}
|
||||
|
||||
/* Redirect stdout/stderr to /dev/null on success */
|
||||
if (status == FUSE_DAEMONIZE_SUCCESS && dm->daemonized) {
|
||||
int nullfd = open("/dev/null", O_RDWR, 0);
|
||||
|
||||
if (nullfd != -1) {
|
||||
(void)dup2(nullfd, 1);
|
||||
(void)dup2(nullfd, 2);
|
||||
if (nullfd > 2)
|
||||
close(nullfd);
|
||||
}
|
||||
}
|
||||
|
||||
close_if_valid(&dm->signal_pipe_wr);
|
||||
close_if_valid(&dm->death_pipe_rd);
|
||||
close_if_valid(&dm->stop_pipe_rd);
|
||||
close_if_valid(&dm->stop_pipe_wr);
|
||||
}
|
||||
|
||||
void fuse_daemonize_early_success(void)
|
||||
{
|
||||
/*
|
||||
* Needs to be gracefully handled as automatically called libfuse
|
||||
* internal from FUSE_INIT handler
|
||||
*/
|
||||
if (!fuse_daemonize_early_is_active())
|
||||
return;
|
||||
|
||||
fuse_daemonize_early_signal(FUSE_DAEMONIZE_SUCCESS);
|
||||
}
|
||||
|
||||
void fuse_daemonize_early_fail(int err)
|
||||
{
|
||||
fuse_daemonize_early_signal(err);
|
||||
}
|
||||
|
||||
bool fuse_daemonize_early_is_active(void)
|
||||
{
|
||||
return daemonize.daemonized || daemonize.active;
|
||||
}
|
||||
|
||||
void fuse_daemonize_early_set_mounted(void)
|
||||
{
|
||||
daemonize.mounted = true;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* FUSE: Filesystem in Userspace
|
||||
* Copyright (C) 2026 Bernd Schubert <bsbernd.com>
|
||||
*
|
||||
* This program can be distributed under the terms of the GNU LGPLv2.
|
||||
* See the file COPYING.LIB.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FUSE_DAEMONIZE_I_H_
|
||||
#define FUSE_DAEMONIZE_I_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Set mounted flag.
|
||||
*
|
||||
* Called from fuse_session_mount().
|
||||
*/
|
||||
void fuse_daemonize_early_set_mounted(void);
|
||||
|
||||
/**
|
||||
* Signal daemonization success to parent and cleanup.
|
||||
*
|
||||
* To be called from the child process after successful mount, when
|
||||
* sychronous FUSE_INIT is used (FUSE_INIT as part of the mount)
|
||||
* Automatically called for async FUSE_INIT.
|
||||
*
|
||||
* Not exposed to the ABI yet, as sync FUSE_INIT is not implemented yet.
|
||||
*/
|
||||
void fuse_daemonize_early_success(void);
|
||||
|
||||
#endif /* FUSE_DAEMONIZE_I_H_ */
|
||||
|
||||
+3
-1
@@ -17,7 +17,6 @@
|
||||
#include <semaphore.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <stdatomic.h>
|
||||
|
||||
#define MIN(a, b) \
|
||||
@@ -110,6 +109,9 @@ struct fuse_session {
|
||||
/* true if reading requests from /dev/fuse are handled internally */
|
||||
bool buf_reallocable;
|
||||
|
||||
/* synchronous FUSE_INIT support */
|
||||
bool want_sync_init;
|
||||
|
||||
/* io_uring */
|
||||
struct fuse_session_uring uring;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "mount_util.h"
|
||||
#include "util.h"
|
||||
#include "fuse_uring_i.h"
|
||||
#include "fuse_daemonize_i.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdatomic.h>
|
||||
@@ -3022,6 +3023,8 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const void *op_in,
|
||||
send_reply_ok(req, &outarg, outargsize);
|
||||
if (enable_io_uring)
|
||||
fuse_uring_wake_ring_threads(se);
|
||||
|
||||
fuse_daemonize_early_success();
|
||||
}
|
||||
|
||||
static __attribute__((no_sanitize("thread"))) void
|
||||
@@ -4454,6 +4457,8 @@ out:
|
||||
se->fd = fd;
|
||||
se->mountpoint = mountpoint;
|
||||
|
||||
fuse_daemonize_early_set_mounted();
|
||||
|
||||
return 0;
|
||||
|
||||
error_out:
|
||||
|
||||
@@ -227,6 +227,11 @@ FUSE_3.19 {
|
||||
fuse_session_start_teardown_watchdog;
|
||||
fuse_session_stop_teardown_watchdog;
|
||||
fuse_lowlevel_notify_prune;
|
||||
fuse_daemonize_early_start;
|
||||
# Not exposed yet for now, as called automatically
|
||||
# fuse_daemonize_early_success;
|
||||
fuse_daemonize_early_fail;
|
||||
fuse_daemonize_early_is_active;
|
||||
} FUSE_3.18;
|
||||
|
||||
# Local Variables:
|
||||
|
||||
+17
-3
@@ -15,6 +15,7 @@
|
||||
#include "fuse_misc.h"
|
||||
#include "fuse_opt.h"
|
||||
#include "fuse_lowlevel.h"
|
||||
#include "fuse_daemonize.h"
|
||||
#include "mount_util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -252,6 +253,12 @@ int fuse_parse_cmdline_30(struct fuse_args *args,
|
||||
|
||||
int fuse_daemonize(int foreground)
|
||||
{
|
||||
/* Check if the NEW API is used */
|
||||
if (fuse_daemonize_early_is_active()) {
|
||||
fuse_log(FUSE_LOG_ERR, "fuse_daemonize_start() already used\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!foreground) {
|
||||
int nullfd;
|
||||
int waiter[2];
|
||||
@@ -279,7 +286,8 @@ int fuse_daemonize(int foreground)
|
||||
break;
|
||||
default:
|
||||
/* parent */
|
||||
(void) read(waiter[0], &completed, sizeof(completed));
|
||||
(void)read(waiter[0], &completed,
|
||||
sizeof(completed));
|
||||
close(waiter[0]);
|
||||
close(waiter[1]);
|
||||
_exit(0);
|
||||
@@ -362,17 +370,23 @@ int fuse_main_real_versioned(int argc, char *argv[],
|
||||
goto out1;
|
||||
}
|
||||
|
||||
struct fuse_session *se = fuse_get_session(fuse);
|
||||
if (fuse_mount(fuse,opts.mountpoint) != 0) {
|
||||
res = 4;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/*
|
||||
* fuse_daemonize() already checks and fails then, but we need to
|
||||
* handle it gracefully here, as this is done libfuse internal
|
||||
* and caller didn't ask to daemonize with old API.
|
||||
*/
|
||||
if (!fuse_daemonize_early_is_active()) {
|
||||
if (fuse_daemonize(opts.foreground) != 0) {
|
||||
res = 5;
|
||||
goto out3;
|
||||
}
|
||||
|
||||
struct fuse_session *se = fuse_get_session(fuse);
|
||||
}
|
||||
if (fuse_set_signal_handlers(se) != 0) {
|
||||
res = 6;
|
||||
goto out3;
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@ libfuse_sources = ['fuse.c', 'fuse_i.h', 'fuse_loop.c', 'fuse_loop_mt.c',
|
||||
'fuse_lowlevel.c', 'fuse_misc.h', 'fuse_opt.c',
|
||||
'fuse_signals.c', 'buffer.c', 'cuse_lowlevel.c',
|
||||
'helper.c', 'modules/subdir.c', 'mount_util.c',
|
||||
'fuse_log.c', 'compat.c', 'util.c', 'util.h' ]
|
||||
'fuse_log.c', 'compat.c', 'util.c', 'util.h',
|
||||
'fuse_daemonize.c' ]
|
||||
|
||||
if host_machine.system().startswith('linux')
|
||||
libfuse_sources += [ 'mount.c' ]
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
|
||||
static void print_conn_info(const char *prefix, struct fuse_conn_info *conn)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user