/* Copyright (C) 2010 Nick Bowler * * License WTFPL2: Do What The Fuck You Want To Public License, version 2. * This is free software: you are free to do what the fuck you want to. * There is NO WARRANTY, to the extent permitted by law. * * Putting . in PATH is generally not a good idea, for a variety of reasons. * However, some programs expect it to be there, and do not function correctly * without it. This program allows one to invoke specific programs in the * current working directory as though . was in the PATH without it actually * being there. * * To work, place a copy of (or symlink to) this program somewhere that is * actually in the PATH. The name of the command determines what program * gets invoked. Basic checks are done to avoid accidental infinite * recursion. * * This is probably not portable to non-GNU/Linux systems. */ #define _GNU_SOURCE #include #include #include #include #include #include #include /* Common shell return codes */ #define SHELL_CMD_NOT_FOUND 127 #define SHELL_CMD_NOT_EXEC 126 /* * A wrapper around readlink which dynamically allocates a buffer large * enough to contain the entire filename. Also add a null terminator * so that the filename can be used as a C string. */ char *readlink_alloc(const char *path) { ssize_t bufsize = 64, rc; char *buf = NULL, *tmp; while (1) { if (bufsize >= SSIZE_MAX/2) { fprintf(stderr, "cmdwrap: %s: %s\n", path, strerror(ENAMETOOLONG)); break; } tmp = realloc(buf, bufsize * 2); if (!tmp) { fprintf(stderr, "cmdwrap: %s\n", strerror(errno)); break; } bufsize *= 2; buf = tmp; rc = readlink(path, buf, bufsize); if (rc == -1) { fprintf(stderr, "cmdwrap: %s: %s\n", path, strerror(errno)); break; } else if (rc < bufsize) { buf[rc] = 0; return buf; } } free(buf); return NULL; } int main(int argc, char **argv) { char *curexe, *newexe, fdname[64]; int fd; if (argc < 1) { fprintf(stderr, "cmdwrap: command name must be present in the argument list.\n"); return SHELL_CMD_NOT_FOUND; } fd = open(argv[0], O_RDONLY); if (fd == -1) { fprintf(stderr, "cmdwrap: %s: %s\n", argv[0], strerror(errno)); return SHELL_CMD_NOT_FOUND; } /* Try to avoid accidental infinite recursion. */ sprintf(fdname, "/proc/self/fd/%d", fd); newexe = readlink_alloc(fdname); if (!newexe) return SHELL_CMD_NOT_FOUND; curexe = readlink_alloc("/proc/self/exe"); if (!curexe) return SHELL_CMD_NOT_FOUND; if (strcmp(newexe, curexe) == 0) { fprintf(stderr, "cmdwrap: infinite recursion detected\n"); return SHELL_CMD_NOT_EXEC; } /* * Some programs expect argv[0] to point to their actual executable * (potentially after a path lookup). But if we leave argv[0] as it * is, they'll tend to find this program instead of the right one. * * Fix that up, too. */ argv[0] = newexe; free(curexe); fexecve(fd, argv, environ); fprintf(stderr, "cmdwrap: %s: %s\n", argv[0], strerror(errno)); return SHELL_CMD_NOT_EXEC; }