Useful CLI tools (bash) for Arch Linux administration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

405 lines
15 KiB

/*
* libetc - Copyright (C) 2005-2008 Luc Dufresne <luc@ordiluc.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* See the file COPYING
*
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>
#include "libetc.h"
// #define DEBUG
#ifdef DEBUG
#define PRINT_DEBUG(args...) fprintf (stderr, args)
#else
#define PRINT_DEBUG(args...)
#endif
static FILE * (*orig_fopen) (const char *path, const char *mode);
static FILE * (*orig_fopen64) (const char *path, const char *mode);
static FILE * (*orig_freopen) (const char *path, const char *mode, FILE *stream);
static FILE * (*orig_freopen64) (const char *path, const char *mode, FILE *stream);
static int (*orig_open) (const char *path, int flags, mode_t mode);
static int (*orig_open64) (const char *path, int flags, mode_t mode);
static int (*orig_mkdir) (const char *path, mode_t mode);
static int (*orig_rename) (const char *oldpath, const char *newpath);
static int (*orig_unlink) (const char *path);
static int (*orig_remove) (const char *path);
static int (*orig_link) (const char *oldpath, const char *newpath);
static int (*orig_symlink) (const char *oldpath, const char *newpath);
static DIR * (*orig_opendir) (const char *name);
static int (*orig_chdir) (const char *path);
static int (*orig___xstat) (int ver, const char *file_name, struct stat *buf);
static int (*orig___xstat64) (int ver, const char *file_name, struct stat64 *buf);
static int (*orig___lxstat) (int ver, const char *file_name, struct stat *buf);
static int (*orig___lxstat64) (int ver, const char *file_name, struct stat64 *buf);
static int (*orig_access) (const char *path, int mode);
static int (*orig_chmod) (const char *path, mode_t mode);
static int (*orig_chown) (const char *path, uid_t owner, gid_t group);
static int (*orig_lchown) (const char *path, uid_t owner, gid_t group);
static int (*orig_utime) (const char *filename, struct utimbuf *buf);
static int (*orig_utimes) (const char *filename, struct timeval *tvp);
static int (*orig_truncate) (const char *path, off_t length);
static int (*orig_truncate64) (const char *path, off_t length);
static int (*orig___xmknod) (int ver, const char *path, mode_t mode, dev_t dev);
static int (*orig_mkfifo) (const char *path, mode_t mode);
static int (*orig_creat) (const char *path, mode_t mode);
static int (*orig_creat64) (const char *path, mode_t mode);
static int (*orig_connect) (int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
static int (*orig_bind) (int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
static int (*orig_readlink) (const char *path, char *buf, size_t bufsiz);
static int (*orig_rmdir) (const char *path);
static int (*orig_mkstemp) (char *template);
static int (*orig_mkstemp64) (char *template);
static char *ETCDIR = ".config";
static char *orig, *home, *etcdir;
static int started = 0;
static int blacklisted = 0;
// is the running exec blacklisted ?
static void am_i_blacklisted () {
char running_exec[4096], *exec_blacklist, *str;
int len;
if ((len = orig_readlink ("/proc/self/exe", running_exec, sizeof (running_exec) - 1))) {
running_exec[len] = '\0';
PRINT_DEBUG ("running exec: %s\n", running_exec);
if ((exec_blacklist = getenv ("LIBETC_BLACKLIST"))) {
PRINT_DEBUG ("blacklist: %s\n", exec_blacklist);
while ((str = strrchr (exec_blacklist, ':'))) {
if (0 == strcmp (++str, running_exec)) {
blacklisted = 1;
PRINT_DEBUG ("I am blacklisted !\n");
}
str--;
str[0] = '\0';
}
if (0 == strcmp (exec_blacklist, running_exec)) {
blacklisted = 1;
PRINT_DEBUG ("I am blacklisted !\n");
}
}
}
}
// find where to put the dotfiles
static void find_etcdir () {
char *etc, *xdg_config_home;
if (!(xdg_config_home = getenv ("XDG_CONFIG_HOME"))) {
if (!(etc = getenv ("ETC"))) {
etc = ETCDIR;
PRINT_DEBUG("default value: %s\n", etc);
}
PRINT_DEBUG("$ETC: %s\n", etc);
etcdir = malloc (strlen (home) + strlen (etc) + 1);
sprintf (etcdir, "%s/%s", home, etc);
} else {
PRINT_DEBUG("$XDG_CONFIG_HOME: %s\n", xdg_config_home);
etcdir = xdg_config_home;
}
}
// mkdir etcdir if it does not exist
static void mkdir_etcdir (const char* etcdir) {
struct stat info;
if (-1 == orig___xstat (_STAT_VER, etcdir, &info)) {
if (orig_mkdir (etcdir, 0700)) {
fprintf (stderr, "Unable to create config directory %s: ", etcdir);
perror ("");
exit (2);
}
} else {
if (!S_ISDIR(info.st_mode)) {
fprintf (stderr, "ERROR: %s exists and is not a directory\n", etcdir);
exit (2);
}
}
}
#ifdef XAUTH_HACK
// ln -s $HOME/.Xauthority $XDG_CONFIG_HOME/Xauthority
static void xauthority_hack (const char* etcdir) {
int len;
char link_dest[4096];
char etcxauth [strlen (etcdir) + strlen ("/Xauthority") + 1];
sprintf (etcxauth, "%s/Xauthority", etcdir );
char homexauth [strlen (home) + strlen ("/.Xauthority") + 1];
sprintf (homexauth, "%s/.Xauthority", home );
if ((len = orig_readlink (etcxauth, link_dest, sizeof (link_dest) -1))) {
link_dest[len] = '\0';
if (0 == strcmp (link_dest, homexauth)) {
PRINT_DEBUG(stderr, "$XDG_CONFIG_HOME/Xauthority link is OK\n");
return; // the link is OK
}
}
PRINT_DEBUG(stderr, "I need to recreate $XDG_CONFIG_HOME/Xauthority link\n");
orig_unlink (etcxauth);
orig_symlink (homexauth, etcxauth);
}
#endif // XAUTH_HACK
// called only once on program startup
static void start_up () {
if (LIKELY (started))
return;
orig_fopen = dlsym (RTLD_NEXT, "fopen");
orig_fopen64 = dlsym (RTLD_NEXT, "fopen64");
orig_freopen = dlsym (RTLD_NEXT, "freopen");
orig_freopen64 = dlsym (RTLD_NEXT, "freopen64");
orig_open = dlsym (RTLD_NEXT, "open");
orig_open64 = dlsym (RTLD_NEXT, "open64");
orig_mkdir = dlsym (RTLD_NEXT, "mkdir");
orig_rename = dlsym (RTLD_NEXT, "rename");
orig_unlink = dlsym (RTLD_NEXT, "unlink");
orig_remove = dlsym (RTLD_NEXT, "remove");
orig_link = dlsym (RTLD_NEXT, "link");
orig_symlink = dlsym (RTLD_NEXT, "symlink");
orig_opendir = dlsym (RTLD_NEXT, "opendir");
orig_chdir = dlsym (RTLD_NEXT, "chdir");
orig___xstat = dlsym (RTLD_NEXT, "__xstat");
orig___xstat64 = dlsym (RTLD_NEXT, "__xstat64");
orig___lxstat = dlsym (RTLD_NEXT, "__lxstat");
orig___lxstat64 = dlsym (RTLD_NEXT, "__lxstat64");
orig_access = dlsym (RTLD_NEXT, "access");
orig_chmod = dlsym (RTLD_NEXT, "chmod");
orig_chown = dlsym (RTLD_NEXT, "chown");
orig_lchown = dlsym (RTLD_NEXT, "lchown");
orig_utime = dlsym (RTLD_NEXT, "utime");
orig_utimes = dlsym (RTLD_NEXT, "utimes");
orig_truncate = dlsym (RTLD_NEXT, "truncate");
orig_truncate64 = dlsym (RTLD_NEXT, "truncate64");
orig___xmknod = dlsym (RTLD_NEXT, "__xmknod");
orig_mkfifo = dlsym (RTLD_NEXT, "mkfifo");
orig_creat = dlsym (RTLD_NEXT, "creat");
orig_creat64 = dlsym (RTLD_NEXT, "creat64");
orig_connect = dlsym (RTLD_NEXT, "connect");
orig_bind = dlsym (RTLD_NEXT, "bind");
orig_readlink = dlsym (RTLD_NEXT, "readlink");
orig_rmdir = dlsym (RTLD_NEXT, "rmdir");
orig_mkstemp = dlsym (RTLD_NEXT, "mkstemp");
orig_mkstemp64 = dlsym (RTLD_NEXT, "mkstemp64");
am_i_blacklisted ();
home = getenv ("HOME");
if (home == NULL) {
started = 1;
return;
}
orig = malloc (strlen (home) + 3);
sprintf (orig, "%s/.", home);
find_etcdir ();
mkdir_etcdir (etcdir);
#ifdef XAUTH_HACK
xauthority_hack (etcdir);
#endif
PRINT_DEBUG("etcdir: %s\n", etcdir);
started = 1;
}
// rename filename if it's a dotfile in $HOME
static char *translate (const char *filename) {
char *wd, *newfilename;
if (UNLIKELY (!started)) start_up();
if (UNLIKELY (home == NULL)) return strdup (filename);
if (UNLIKELY (blacklisted)) return strdup (filename);
if (UNLIKELY (!filename)) {
PRINT_DEBUG("Filename is NULL !\n");
return NULL;
}
wd = get_current_dir_name ();
if ((0 == strcmp (wd, home)) // if cwd == $HOME
&& filename [0] == '.' // and a dotfile
&& (0 != strcmp (filename, "."))
&& (0 != strncmp (filename, "./", 2))
&& (0 != strncmp (filename, "..", 2))) {
char tmpfilename [strlen (home) + strlen (filename) + 2];
sprintf (tmpfilename, "%s/%s", home, filename);
if (0 == strncmp (tmpfilename, etcdir, strlen(etcdir))) { // do not translate if trying to read/write in $XDG_CONFIG_HOME
newfilename = strdup (filename);
} else {
filename++; // remove the dot
newfilename = malloc (strlen (filename) + strlen (etcdir) + 2);
sprintf (newfilename, "%s/%s", etcdir, filename);
PRINT_DEBUG("RENAMED IN $HOME --> %s\n", newfilename);
}
} else if (0 == strncmp (filename, orig, strlen (orig)) // if file name is $HOME/.something
&& 0!= strncmp (filename, etcdir, strlen (etcdir)) ) { // do not translate if trying to read/write in $XDG_CONFIG_HOME
filename += strlen (home) + 2; // remove "$HOME/." from the filename
newfilename = malloc (strlen (filename) + strlen (etcdir) + 2);
sprintf (newfilename, "%s/%s", etcdir, filename);
PRINT_DEBUG("RENAMED --> %s\n", newfilename);
} else { // not a dotfile
newfilename = strdup (filename);
}
free (wd);
return newfilename;
}
#define REWRITE_FUNCTION_SIMPLE(return_type, function_name, signature, orig_call) \
return_type function_name signature { \
return_type return_value; \
char *new_path; \
\
PRINT_DEBUG(#function_name ": %s\n", path); \
\
new_path = translate (path); \
return_value = orig_##function_name orig_call; \
free (new_path); \
return return_value; \
}
//#define NPATH NEWPATH(new_path, path)
REWRITE_FUNCTION_SIMPLE(FILE*, fopen, (const char *path, const char *mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(FILE*, fopen64, (const char *path, const char *mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(FILE*, freopen, (const char *path, const char *mode, FILE *stream), (new_path, mode, stream))
REWRITE_FUNCTION_SIMPLE(FILE*, freopen64, (const char *path, const char *mode, FILE *stream), (new_path, mode, stream))
REWRITE_FUNCTION_SIMPLE(int, open, (const char *path, int flags, mode_t mode), (new_path, flags, mode))
REWRITE_FUNCTION_SIMPLE(int, open64, (const char *path, int flags, mode_t mode), (new_path, flags, mode))
REWRITE_FUNCTION_SIMPLE(int, mkdir, (const char *path, mode_t mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, unlink, (const char *path), (new_path))
REWRITE_FUNCTION_SIMPLE(int, remove, (const char *path), (new_path))
REWRITE_FUNCTION_SIMPLE(DIR*, opendir, (const char *path), (new_path))
REWRITE_FUNCTION_SIMPLE(int, chdir, (const char *path), (new_path))
REWRITE_FUNCTION_SIMPLE(int, __xstat, (int ver, const char *path, struct stat *buf), (ver, new_path, buf))
REWRITE_FUNCTION_SIMPLE(int, __xstat64, (int ver, const char *path, struct stat64 *buf), (ver, new_path, buf))
REWRITE_FUNCTION_SIMPLE(int, __lxstat, (int ver, const char *path, struct stat *buf), (ver, new_path, buf))
REWRITE_FUNCTION_SIMPLE(int, __lxstat64, (int ver, const char *path, struct stat64 *buf), (ver, new_path, buf))
REWRITE_FUNCTION_SIMPLE(int, access, (const char *path, int mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, chmod, (const char *path, mode_t mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, chown, (const char *path, uid_t owner, gid_t group), (new_path, owner, group))
REWRITE_FUNCTION_SIMPLE(int, lchown, (const char *path, uid_t owner, gid_t group), (new_path, owner, group))
REWRITE_FUNCTION_SIMPLE(int, utime, (const char *path, struct utimbuf *buf), (new_path, buf))
REWRITE_FUNCTION_SIMPLE(int, utimes, (const char *path, struct timeval *tvp), (new_path, tvp))
REWRITE_FUNCTION_SIMPLE(int, truncate, (const char *path, off_t length), (new_path, length))
REWRITE_FUNCTION_SIMPLE(int, truncate64, (const char *path, off_t length), (new_path, length))
REWRITE_FUNCTION_SIMPLE(int, __xmknod, (int ver, const char *path, mode_t mode, dev_t dev), (ver, new_path, mode, dev))
REWRITE_FUNCTION_SIMPLE(int, mkfifo, (const char *path, mode_t mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, creat, (const char *path, mode_t mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, creat64, (const char *path, mode_t mode), (new_path, mode))
REWRITE_FUNCTION_SIMPLE(int, readlink, (const char *path, char *buf, size_t bufsize), (new_path, buf, bufsize))
REWRITE_FUNCTION_SIMPLE(int, rmdir, (const char *path), (new_path))
#define REWRITE_FUNCTION_DOUBLE(return_type, function_name, signature, orig_call) \
return_type function_name signature { \
return_type return_value; \
char *new_path1, *new_path2; \
\
PRINT_DEBUG(#function_name ": %s %s\n", path1, path2); \
\
new_path1 = translate (path1); \
new_path2 = translate (path2); \
return_value = orig_##function_name orig_call; \
free (new_path1); \
free (new_path2); \
return return_value; \
}
REWRITE_FUNCTION_DOUBLE(int, rename, (const char *path1, const char *path2), (new_path1, new_path2))
REWRITE_FUNCTION_DOUBLE(int, link, (const char *path1, const char *path2), (new_path1, new_path2))
REWRITE_FUNCTION_DOUBLE(int, symlink, (const char *path1, const char *path2), (new_path1, new_path2))
#define REWRITE_FUNCTION_MKSTEMP(function_name) \
int function_name(char *path) { \
int return_value, i; \
char *new_path; \
size_t size, new_size; \
\
PRINT_DEBUG(#function_name ": %s\n", path); \
\
new_path = translate (path); \
if(LIKELY(new_path != NULL)) { \
return_value = orig_##function_name (new_path); \
size = strlen (path); \
new_size = strlen (new_path); \
for (i = 0; i <= 6; i++) { \
path[size - i] = new_path[new_size - i]; \
} \
free(new_path); \
return return_value; \
} \
else { \
return orig_##function_name (path); \
} \
} \
REWRITE_FUNCTION_MKSTEMP(mkstemp);
REWRITE_FUNCTION_MKSTEMP(mkstemp64);
#define REWRITE_FUNCTION_SOCK(function_name) \
int function_name(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) { \
int offset; \
struct sockaddr_un newaddr; \
char *path, *new_path; \
int return_value; \
\
if (serv_addr->sa_family == AF_LOCAL) { \
path = ((struct sockaddr_un*)serv_addr)->sun_path; \
offset = (path[0] == '\0'); \
path += offset; \
new_path = translate (path); \
\
PRINT_DEBUG(#function_name": %s\n", path); \
\
if(LIKELY(new_path != NULL)) { \
newaddr.sun_family = AF_LOCAL; \
newaddr.sun_path[0] = '\0'; \
strncpy (newaddr.sun_path + offset, new_path, sizeof (newaddr.sun_path) - offset); \
newaddr.sun_path[sizeof (newaddr.sun_path) - 1] = '\0'; \
return_value = orig_##function_name (sockfd, (struct sockaddr*) &newaddr, \
offsetof (struct sockaddr_un, sun_path) + strlen(new_path) + offset); \
free (new_path); \
return return_value; \
} \
else { \
return orig_##function_name (sockfd, serv_addr, addrlen); \
} \
} else { \
if (UNLIKELY (!started)) start_up(); \
return orig_##function_name (sockfd, serv_addr, addrlen); \
} \
}
REWRITE_FUNCTION_SOCK(connect);
REWRITE_FUNCTION_SOCK(bind);