Skip to content

Instantly share code, notes, and snippets.

@nurse
Last active September 26, 2015 16:57
Show Gist options
  • Save nurse/1129437 to your computer and use it in GitHub Desktop.
Save nurse/1129437 to your computer and use it in GitHub Desktop.
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",
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment