ossp-pkg/path/path_self.c
/*
** OSSP path - Filesystem Path Manipulation
** Copyright (c) 2002-2003 Ralf S. Engelschall <rse@engelschall.com>
** Copyright (c) 2002-2003 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2003 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
** This file is part of OSSP path, a filesystem path manipulation library
** which can be found at http://www.ossp.org/pkg/lib/path/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** path_self.c: resolve path to current program
*/
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__FreeBSD__)
#include <sys/sysctl.h>
#include <limits.h>
#endif
#include <unistd.h>
#include "path.h"
#define PATH_DEFAULT "/bin:/sbin:/usr/bin:/usr/sbin:."
path_rc_t path_self(char *res_buf, size_t res_size, const char *argv0)
{
char *path;
size_t l;
struct stat sb;
char *cpB, *cpE;
size_t argv0_len;
#if defined(__FreeBSD__)
int mib[4];
#endif
if (res_buf == NULL || res_size == 0 || argv0 == NULL)
return PATH_ERR_ARG;
#if defined(__FreeBSD__)
/* retrieve path via sysctl(3) (FreeBSD only) */
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
l = res_size - 1;
if (sysctl(mib, 4, res_buf, &l, NULL, 0) == 0) {
res_buf[l] = '\0';
if (path_resolve(res_buf, res_size, res_buf) == NULL)
return PATH_ERR_SYS;
}
#endif
/* search for /proc entries (Linux only) */
if ((l = readlink("/proc/self/exe" /* Linux */, res_buf, res_size-1)) == -1)
if ((l = readlink("/proc/self/path/a.out" /* Solaris */, res_buf, res_size-1)) == -1)
l = readlink("/proc/curproc/file" /* BSD */, res_buf, res_size-1);
if (l > 0) {
if (res_buf[l-1] == '\0')
l--;
res_buf[l] = '\0';
if (strcmp(res_buf, "unknown") != 0)
if (path_resolve(res_buf, res_size, res_buf) == NULL)
return PATH_ERR_SYS;
}
/* determine length of argv[0] */
argv0_len = strlen(argv0);
/* short-circuit if argv[0] already contains an absolute path */
if (argv0[0] == '/') {
if (argv0_len > res_size-1)
return PATH_ERR_MEM;
memcpy(res_buf, argv0, argv0_len);
res_buf[argv0_len] = '\0';
if (path_resolve(res_buf, res_size, res_buf) == NULL)
return PATH_ERR_SYS;
return PATH_OK;
}
/* short-circuit if argv[0] already contains a relative path */
if (argv0[0] == '.') {
if (getcwd(res_buf, res_size) == NULL)
return PATH_ERR_SYS;
l = strlen(res_buf);
if (res_buf[l-1] == '/')
l--;
if (l+1+argv0_len+1 > res_size)
return PATH_ERR_MEM;
res_buf[l++] = '/';
memcpy(res_buf+l, argv0, argv0_len);
res_buf[l+argv0_len] = '\0';
if (path_resolve(res_buf, res_size, res_buf) == NULL)
return PATH_ERR_SYS;
return PATH_OK;
}
/* else search argv[0] in $PATH */
if ((path = getenv("PATH")) == NULL)
path = PATH_DEFAULT;
cpE = path;
cpB = cpE;
while (*cpE != '\0') {
if ((cpE = strchr(cpB, ':')) == NULL)
cpE = strchr(cpB, '\0');
if ((l = cpE-cpB) > 0) {
if (cpB[l-1] == '/')
l--;
if (l+1+argv0_len+1 <= res_size) {
memcpy(res_buf, cpB, l);
res_buf[l++] = '/';
memcpy(res_buf+l, argv0, argv0_len);
res_buf[l+argv0_len] = '\0';
if (stat(res_buf, &sb) == 0) {
if (path_resolve(res_buf, res_size, res_buf) == NULL)
return PATH_ERR_SYS;
return PATH_OK;
}
}
}
if (*cpE == '\0')
break;
cpB = cpE+1;
}
return PATH_ERR_NFD;
}