Skip to content

Instantly share code, notes, and snippets.

@nurse

nurse/gist:1129437

Last active Sep 26, 2015
Embed
What would you like to do?
Get the path of ruby binary
diff --git a/ext/etc/etc.c b/ext/etc/etc.c
index 2bd2e30..9da0ed7 100644
--- a/ext/etc/etc.c
+++ b/ext/etc/etc.c
@@ -597,6 +597,155 @@ etc_systmpdir(void)
return tmpdir;
}
+#ifdef _WIN32
+# define RUBYPATH_GetModuleFileName
+#elif defined(__APPLE__)
+# define RUBYPATH_NSGetExecutablePath
+# include <mach-o/dyld.h>
+#elif defined(__sun__)
+# define RUBYPATH_getexecname
+#elif defined(_AIX) || defined(__OpenBSD__)
+# define RUBYPATH_dladdr
+# include <dlfcn.h>
+#elif defined(__FreeBSD__)
+# define RUBYPATH_sysctl
+# include <sys/sysctl.h>
+# include <errno.h>
+#elif defined(__linux__) || defined(__NetBSD__) || defined(__DragonFly__)
+# define RUBYPATH_procfs
+#endif
+
+/*
+ * call-seq:
+ * Etc.rubypath -> string
+ *
+ * Returns the real absolute path of current executing ruby.
+ * "real" means it doesn't include symlinks.
+ * If the environment doesn't support this, raises NotImplementedError.
+ *
+ * Example in shell:
+ * % pwd
+ * /hoge
+ * % ls -l
+ * lrwxr-xr-x 1 rubyist users 32 Jan 1 00:00 foo -> /usr/local/bin/ruby
+ * % ./foo -retc -e'puts Etc.rubypath'
+ * /usr/local/bin/ruby
+ * % cd /
+ * % PATH=/hoge foo -retc -e'puts Etc.rubypath'
+ * /usr/local/bin/ruby
+ */
+static VALUE
+etc_rubypath(void)
+{
+#ifdef RUBYPATH_GetModuleFileName
+ char buf[4096];
+ size_t len;
+ len = (size_t)GetModuleFileName(NULL, buf, sizeof(buf));
+ if (len == 0) rb_sys_fail("Can't get the path of ruby");
+ return rb_filesystem_str_new(buf, len);
+#elif defined(RUBYPATH_NSGetExecutablePath)
+ /*
+ * defined(__APPLE__): returns absolute but symlink; need realpath
+ */
+ VALUE v;
+ uint32_t len = 0;
+ char *buf, *resolved
+ int err;
+ _NSGetExecutablePath(NULL, &len);
+ buf = malloc(len);
+ if (buf == NULL) rb_sys_fail("Can't allocate memory");
+ err = _NSGetExecutablePath(buf, &len);
+ if (err != 0) rb_sys_fail("Can't get the path of ruby");
+ resolved = realpath(buf, NULL);
+ if (resolved == NULL) rb_sys_fail("Can't get the path of ruby");
+ v = rb_filesystem_str_new_cstr(resolved);
+ free(buf);
+ free(resolved);
+ return v;
+#elif defined(RUBYPATH_getexecname)
+ const char *name = getexecname();
+ if (name == NULL) rb_sys_fail("Can't get the path of ruby");
+ return rb_filesystem_str_new_cstr(name);
+#elif defined(RUBYPATH_dladdr)
+ /*
+ * defined(__FreeBSD__): returns absolute but symlink; need realpath
+ * defined(__NetBSD__): returns relative; can't use
+ * defined(__OpenBSD__): returns relative; can't use
+ * defined(__DragonFly__): returns relative; can't use
+ * defined(__sun__):
+ * defined(_AIX):
+ * defined(__APPLE__): returns absolute but symlink; need realpath
+ *
+ * On relative platform, dln_find_exe can be used.
+ */
+ VALUE v;
+ char *resolved;
+ Dl_info info;
+ extern char **environ; /* environ should be in "ruby" not libruby */
+ if (dladdr(&environ, &info) == 0)
+ rb_sys_fail("Can't get the path of ruby");
+ resolved = realpath(info.dli_fname, NULL); /* realpath(, NULL) is platform dependent */
+ if (resolved == NULL) rb_sys_fail("Can't get the path of ruby");
+ v = rb_filesystem_str_new_cstr(resolved);
+ free(resolved);
+ return v;
+#elif defined(RUBYPATH_sysctl)
+ /*
+ * defined(__FreeBSD__): returns absolute
+ */
+ VALUE v;
+ int mib[4], err;
+ char *buf;
+ size_t len = 0;
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PATHNAME;
+ mib[3] = -1;
+ err = sysctl(mib, 4, NULL, &len, NULL, 0);
+ if (err) rb_sys_fail("Can't get the path of ruby");
+ buf = malloc(len);
+ if (buf == NULL) rb_sys_fail("Can't allocate memory");
+ err = sysctl(mib, 4, buf, &len, NULL, 0);
+ if (err) {
+ free(buf);
+ rb_sys_fail("Can't get the path of ruby");
+ }
+ len--;
+ v = rb_filesystem_str_new(buf, len);
+ free(buf);
+ return v;
+#elif defined(RUBYPATH_procfs)
+ /*
+ * defined(__linux__): returns absolute
+ * defined(__NetBSD__): returns absolute
+ * defined(__DragonFly__): returns absolute
+ */
+ VALUE v;
+ size_t bufsiz = PATH_MAX;
+ char buf[PATH_MAX];
+ ssize_t len = 0;
+# if defined(__linux__) /* solaris 10+ */
+ len = readlink("/proc/self/exe", buf, bufsiz);
+# elif defined(__DragonFly__)
+ len = readlink("/proc/curproc/file", buf, bufsiz);
+# elif defined(__NetBSD__)
+ len = readlink("/proc/curproc/exe", buf, bufsiz);
+# else
+ rb_notimplement();
+# endif
+ if (len < 0) {
+ rb_sys_fail("Can't get the path of ruby");
+ }
+ /* assume PATH_MAX is enough length */
+ v = rb_filesystem_str_new(buf, len);
+ return v;
+#else
+ rb_notimplement();
+ return Qnil; /* dummy */
+#endif
+}
+
/*
* The etc module provides access to information from the running OS.
*
@@ -625,6 +774,7 @@ Init_etc(void)
rb_define_module_function(mEtc, "getgrent", etc_getgrent, 0);
rb_define_module_function(mEtc, "sysconfdir", etc_sysconfdir, 0);
rb_define_module_function(mEtc, "systmpdir", etc_systmpdir, 0);
+ rb_define_module_function(mEtc, "rubypath", etc_rubypath, 0);
sPasswd = rb_struct_define("Passwd",
"name", "passwd", "uid", "gid",
@nurse

This comment has been minimized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.