diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 26c4c4df9..ef936e247 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -29,8 +30,6 @@ char *bitcoin_datadir; -#define FIXME_IMPLEMENT() errx(1, "FIXME: Implement %s", __func__) - struct peer *find_peer_by_unique_id(struct lightningd *ld, u64 unique_id) { struct peer *peer; @@ -137,11 +136,50 @@ static void test_daemons(const struct lightningd *ld) static const char *find_my_path(const tal_t *ctx, const char *argv0) { - char *me = path_canon(ctx, argv0); + char *me, *tmpctx = tal_tmpctx(ctx); + + /* FIXME: Expose in CCAN! */ +#define PATH_SEP_STR "/" +#define PATH_SEP (PATH_SEP_STR[0]) + + if (strchr(argv0, PATH_SEP)) { + const char *path; + /* Absolute paths are easy. */ + if (strstarts(argv0, PATH_SEP_STR)) + path = argv0; + /* It contains a '/', it's relative to current dir. */ + else + path = path_join(tmpctx, path_cwd(tmpctx), argv0); + + me = path_canon(ctx, path); + if (!me || access(me, X_OK) != 0) + errx(1, "I cannot find myself at %s based on my name %s", + path, argv0); + } else { + /* No /, search path */ + char **pathdirs; + const char *pathenv = getenv("PATH"); + size_t i; + + if (!pathenv) + errx(1, "Cannot find myself: no $PATH set"); + + pathdirs = tal_strsplit(tmpctx, pathenv, ":", STR_NO_EMPTY); + me = NULL; + for (i = 0; pathdirs[i]; i++) { + /* This returns NULL if it doesn't exist. */ + me = path_canon(ctx, + path_join(tmpctx, pathdirs[i], argv0)); + if (me && access(me, X_OK) == 0) + break; + /* Nope, try again. */ + me = tal_free(me); + } + if (!me) + errx(1, "Cannot find %s in $PATH", argv0); + } - if (access(me, X_OK) != 0) - errx(1, "I cannot find myself at %s based on my name %s", - me, argv0); + tal_free(tmpctx); return path_dirname(ctx, take(me)); } diff --git a/lightningd/log.h b/lightningd/log.h index a825b3aad..c0d788a28 100644 --- a/lightningd/log.h +++ b/lightningd/log.h @@ -27,8 +27,7 @@ struct log_book *new_log_book(const tal_t *ctx, enum log_level printlevel); /* With different entry points */ -struct log *PRINTF_FMT(3,4) -new_log(const tal_t *ctx, struct log_book *record, const char *fmt, ...); +struct log *new_log(const tal_t *ctx, struct log_book *record, const char *fmt, ...) PRINTF_FMT(3,4); #define log_debug(log, ...) log_((log), LOG_DBG, __VA_ARGS__) #define log_info(log, ...) log_((log), LOG_INFORM, __VA_ARGS__) diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c new file mode 100644 index 000000000..ff338019c --- /dev/null +++ b/lightningd/test/run-find_my_path.c @@ -0,0 +1,119 @@ +#define main unused_main +int unused_main(int argc, char *argv[]); + +#include "../lightningd.c" + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for crashlog_activate */ +void crashlog_activate(const char *argv0 UNNEEDED, struct log *log UNNEEDED) +{ fprintf(stderr, "crashlog_activate called!\n"); abort(); } +/* Generated stub for gossip_init */ +void gossip_init(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "gossip_init called!\n"); abort(); } +/* Generated stub for handle_opts */ +bool handle_opts(struct lightningd *ld UNNEEDED, int argc UNNEEDED, char *argv[]) +{ fprintf(stderr, "handle_opts called!\n"); abort(); } +/* Generated stub for hash_htlc_key */ +size_t hash_htlc_key(const struct htlc_key *htlc_key UNNEEDED) +{ fprintf(stderr, "hash_htlc_key called!\n"); abort(); } +/* Generated stub for hsm_init */ +void hsm_init(struct lightningd *ld UNNEEDED, bool newdir UNNEEDED) +{ fprintf(stderr, "hsm_init called!\n"); abort(); } +/* Generated stub for invoices_init */ +struct invoices *invoices_init(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "invoices_init called!\n"); abort(); } +/* Generated stub for log_ */ +void log_(struct log *log UNNEEDED, enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "log_ called!\n"); abort(); } +/* Generated stub for new_log */ +struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED, const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "new_log called!\n"); abort(); } +/* Generated stub for new_log_book */ +struct log_book *new_log_book(const tal_t *ctx UNNEEDED, + size_t max_mem UNNEEDED, + enum log_level printlevel UNNEEDED) +{ fprintf(stderr, "new_log_book called!\n"); abort(); } +/* Generated stub for new_topology */ +struct chain_topology *new_topology(const tal_t *ctx UNNEEDED, struct log *log UNNEEDED) +{ fprintf(stderr, "new_topology called!\n"); abort(); } +/* Generated stub for opt_subd_debug */ +char *opt_subd_debug(const char *optarg UNNEEDED, struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "opt_subd_debug called!\n"); abort(); } +/* Generated stub for opt_subd_dev_disconnect */ +char *opt_subd_dev_disconnect(const char *optarg UNNEEDED, struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "opt_subd_dev_disconnect called!\n"); abort(); } +/* Generated stub for populate_peer */ +void populate_peer(struct lightningd *ld UNNEEDED, struct peer *peer UNNEEDED) +{ fprintf(stderr, "populate_peer called!\n"); abort(); } +/* Generated stub for register_opts */ +void register_opts(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "register_opts called!\n"); abort(); } +/* Generated stub for setup_jsonrpc */ +void setup_jsonrpc(struct lightningd *ld UNNEEDED, const char *rpc_filename UNNEEDED) +{ fprintf(stderr, "setup_jsonrpc called!\n"); abort(); } +/* Generated stub for setup_listeners */ +void setup_listeners(struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "setup_listeners called!\n"); abort(); } +/* Generated stub for setup_topology */ +void setup_topology(struct chain_topology *topology UNNEEDED, + struct timers *timers UNNEEDED, + struct timerel poll_time UNNEEDED, u32 first_peer_block UNNEEDED) +{ fprintf(stderr, "setup_topology called!\n"); abort(); } +/* Generated stub for subd_shutdown */ +void subd_shutdown(struct subd *subd UNNEEDED, unsigned int seconds UNNEEDED) +{ fprintf(stderr, "subd_shutdown called!\n"); abort(); } +/* Generated stub for timer_expired */ +void timer_expired(tal_t *ctx UNNEEDED, struct timer *timer UNNEEDED) +{ fprintf(stderr, "timer_expired called!\n"); abort(); } +/* Generated stub for version */ +const char *version(void) +{ fprintf(stderr, "version called!\n"); abort(); } +/* Generated stub for wallet_channels_load_active */ +bool wallet_channels_load_active(struct wallet *w UNNEEDED, struct list_head *peers UNNEEDED) +{ fprintf(stderr, "wallet_channels_load_active called!\n"); abort(); } +/* Generated stub for wallet_new */ +struct wallet *wallet_new(const tal_t *ctx UNNEEDED, struct log *log UNNEEDED) +{ fprintf(stderr, "wallet_new called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +#undef main +int main(int argc, char *argv[]) +{ + char *tmpctx = tal_tmpctx(NULL); + char *argv0; + /* We're assuming we're run from top build dir. */ + const char *answer = path_canon(tmpctx, "lightningd/test"); + + /* Various different ways we could find ourselves. */ + argv0 = path_join(tmpctx, + path_cwd(tmpctx), "lightningd/test/run-find_my_path"); + unsetenv("PATH"); + + /* Absolute path. */ + assert(streq(find_my_path(tmpctx, argv0), answer)); + + /* Relative to cwd. */ + argv0 = "lightningd/test/run-find_my_path"; + assert(streq(find_my_path(tmpctx, argv0), answer)); + + /* Using $PATH */ + setenv("PATH", path_join(tmpctx, + path_cwd(tmpctx), "lightningd/test"), 1); + argv0 = "run-find_my_path"; + + assert(streq(find_my_path(tmpctx, argv0), answer)); + + /* Even with dummy things in path. */ + char **pathelems = tal_arr(tmpctx, char *, 4); + pathelems[0] = "/tmp/foo"; + pathelems[1] = "/sbin"; + pathelems[2] = path_join(tmpctx, path_cwd(tmpctx), "lightningd/test"); + pathelems[3] = NULL; + + setenv("PATH", tal_strjoin(tmpctx, pathelems, ":", STR_NO_TRAIL), 1); + assert(streq(find_my_path(tmpctx, argv0), answer)); + + tal_free(tmpctx); + return 0; +}