/* -*-pgsql-c-*- */ /* * * $Header$ * * pgpool: a language independent connection pool server for PostgreSQL * written by Tatsuo Ishii * * Portions Copyright (c) 2003-2017, PgPool Global Development Group * Portions Copyright (c) 2004, PostgreSQL Global Development Group * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that copyright notice and this permission * notice appear in supporting documentation, and that the name of the * author not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. The author makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * pool_path.c.: small functions to manipulate paths * */ #include "pool.h" #include "utils/pool_path.h" #include #include #include #include #include #include "utils/elog.h" #include "utils/palloc.h" #include "utils/memutils.h" static void trim_directory(char *path); static void trim_trailing_separator(char *path); static int pqGetpwuid(uid_t uid, struct passwd *resultbuf, char *buffer, size_t buflen, struct passwd **result); /* * get_parent_directory * * Modify the given string in-place to name the parent directory of the * named file. */ void get_parent_directory(char *path) { trim_directory(path); } /* * Obtain user's home directory, return in given buffer */ bool get_home_directory(char *buf, int bufsize) { char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pwd = NULL; if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0) return false; strlcpy(buf, pwd->pw_dir, bufsize); return true; } /* * Obtain user name, return in given buffer */ bool get_os_username(char *buf, int bufsize) { char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pwd = NULL; if (pqGetpwuid(geteuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) != 0) return false; strlcpy(buf, pwd->pw_name, bufsize); return true; } /* stolen from PostgreSQL src/port/thread.c * XXX: This is from 9.4 but the latest pgsql have changed this. */ /* * Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r() * behaviour, if it is not available or required. */ static int pqGetpwuid(uid_t uid, struct passwd *resultbuf, char *buffer, size_t buflen, struct passwd **result) { #if defined(HAVE_GETPWUID_R) #ifdef GETPWUID_R_5ARG /* POSIX version */ getpwuid_r(uid, resultbuf, buffer, buflen, result); #else /* * Early POSIX draft of getpwuid_r() returns 'struct passwd *'. * getpwuid_r(uid, resultbuf, buffer, buflen) */ *result = getpwuid_r(uid, resultbuf, buffer, buflen); #endif #else /* no getpwuid_r() available, just use getpwuid() */ *result = getpwuid(uid); #endif return (*result == NULL) ? -1 : 0; } /* * trim_directory * * Trim trailing directory from path, that is, remove any trailing slashes, * the last pathname component, and the slash just ahead of it --- but never * remove a leading slash. */ static void trim_directory(char *path) { char *p; if (path[0] == '\0') return; /* back up over trailing slash(es) */ for (p = path + strlen(path) - 1; IS_DIR_SEP(*p) && p > path; p--); /* back up over directory name */ for (; !IS_DIR_SEP(*p) && p > path; p--); /* if multiple slashes before directory name, remove 'em all */ for (; p > path && IS_DIR_SEP(*(p - 1)); p--); /* don't erase a leading slash */ if (p == path && IS_DIR_SEP(*p)) p++; *p = '\0'; } /* * join_path_components - join two path components, inserting a slash * * ret_path is the output area (must be of size MAXPGPATH) * * ret_path can be the same as head, but not the same as tail. */ void join_path_components(char *ret_path, const char *head, const char *tail) { if (ret_path != head) StrNCpy(ret_path, head, MAXPGPATH); /* * Remove any leading "." and ".." in the tail component, adjusting head * as needed. */ for (;;) { if (tail[0] == '.' && IS_DIR_SEP(tail[1])) { tail += 2; } else if (tail[0] == '.' && tail[1] == '\0') { tail += 1; break; } else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2])) { trim_directory(ret_path); tail += 3; } else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0') { trim_directory(ret_path); tail += 2; break; } else break; } if (*tail) snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path), "/%s", tail); } /* * Clean up path by: * o remove trailing slash * o remove duplicate adjacent separators * o remove trailing '.' * o process trailing '..' ourselves */ void canonicalize_path(char *path) { char *p, *to_p; bool was_sep = false; /* * Removing the trailing slash on a path means we never get ugly double * trailing slashes. */ trim_trailing_separator(path); /* * Remove duplicate adjacent separators */ p = path; to_p = p; for (; *p; p++, to_p++) { /* Handle many adjacent slashes, like "/a///b" */ while (*p == '/' && was_sep) p++; if (to_p != p) *to_p = *p; was_sep = (*p == '/'); } *to_p = '\0'; /* * Remove any trailing uses of "." and process ".." ourselves */ for (;;) { int len = strlen(path); if (len > 2 && strcmp(path + len - 2, "/.") == 0) trim_directory(path); else if (len > 3 && strcmp(path + len - 3, "/..") == 0) { trim_directory(path); trim_directory(path); /* remove directory above */ } else break; } } /* * last_dir_separator * * Find the location of the last directory separator, return * NULL if not found. */ char * last_dir_separator(const char *filename) { const char *p, *ret = NULL; for (p = filename; *p; p++) if (IS_DIR_SEP(*p)) ret = p; return (char *) ret; } /* * trim_trailing_separator * * trim off trailing slashes, but not a leading slash */ static void trim_trailing_separator(char *path) { char *p; p = path + strlen(path); if (p > path) for (p--; p > path && IS_DIR_SEP(*p); p--) *p = '\0'; } char * get_current_working_dir(void) { char *buf = NULL; size_t buflen = MAXPGPATH; for (;;) { buf = palloc(buflen); if (getcwd(buf, buflen)) break; else if (errno == ERANGE) { pfree(buf); buflen *= 2; continue; } else { int save_errno = errno; pfree(buf); errno = save_errno; ereport(ERROR, (errmsg("could not get current working directory: %m"))); return NULL; } } return buf; } /* * make_absolute_path * * If the given pathname isn't already absolute, make it so, interpreting * it relative to the current working directory. * * if arg base_dir is NULL, The function gets the current cwd * Also canonicalizes the path. The result is always a palloc'd copy. * * Logic borrowed from PostgreSQL source */ char * make_absolute_path(const char *path, const char *base_dir) { char *new; /* Returning null for null input is convenient for some callers */ if (path == NULL) return NULL; if (!is_absolute_path(path)) { const char *cwd = NULL; if (base_dir == NULL) { cwd = get_current_working_dir(); if (!cwd) return NULL; } else cwd = base_dir; new = palloc(strlen(cwd) + strlen(path) + 2); sprintf(new, "%s/%s", cwd, path); if (!base_dir) pfree((void *) cwd); } else { new = pstrdup(path); } /* Make sure punctuation is canonical, too */ canonicalize_path(new); return new; }