1 module goinsu;
2 
3 import core.sys.posix.sys.types : uid_t, gid_t;
4 
5 extern(C) @nogc nothrow:
6 
7 import core.stdc.errno : errno;
8 
9 int setgroups(size_t size, const(gid_t)* list);
10 int getgrouplist(const(char)* user, gid_t group, gid_t* groups, int* ngroups);
11 
12 auto getByNameOrId(alias byid, T, alias byname)(char* v) {
13 	import core.stdc.stdlib : strtol;
14 	char* end;
15 	auto i = v.strtol(&end, 10);
16 	return *end == '\0' ? byid(cast(T) i) : byname(v);
17 }
18 
19 void fail(alias err = -1, A...)(in string fmt, A args) {
20 	import core.stdc.stdio : stderr, fprintf;
21 	import core.stdc.stdlib : exit;
22 	stderr.fprintf(fmt.ptr, args);
23 	stderr.fprintf("\n");
24 	exit(err);
25 }
26 
27 static if(__traits(compiles, {import version_; string v = VERSION;})) {
28 	import version_;
29 } else {
30 	enum VERSION="(unknown version)";
31 }
32 
33 int main(int argc, char** argv) {
34 	import core.sys.posix.unistd : getuid, getgid, execvp, setgid, setuid;
35 	import core.stdc.string : strerror, strchr;
36 
37 	if(argc < 3)
38 		("Usage: %s user-spec command [args]\ngoinsu " ~ VERSION).fail!(0)(argv[0]);
39 
40 	auto user = argv[1];
41 	auto group = user.strchr(':');
42 	if(group)
43 		*group++ = '\0'; // "user:group\0" ====> "user\0group\0"
44 
45 	import core.sys.posix.pwd : getpwuid, getpwnam;
46 	auto pw = user.getByNameOrId!(getpwuid, uid_t, getpwnam);
47 
48 	if(pw is null && errno)
49 		"Error while getting user '%s': %s"
50 			.fail!errno(user, errno.strerror);
51 
52 	if(pw is null)
53 		"User '%s' doesn't exist".fail(user);
54 
55 	immutable uid = pw.pw_uid;
56 	auto gid = pw.pw_gid;
57 
58 	if(group && group[0] != '\0') {
59 		import core.sys.posix.grp : getgrgid, getgrnam;
60 
61 		auto gr = group.getByNameOrId!(getgrgid, gid_t, getgrnam);
62 		if(gr is null && errno)
63 			"Error while getting group '%s': %s"
64 				.fail!errno(group, user, errno.strerror);
65 
66 		if(gr is null)
67 			"Group '%s' doesn't exist".fail(group);
68 
69 		gid = gr.gr_gid;
70 	}
71 
72 	import core.sys.posix.stdlib : setenv;
73 	"HOME".setenv(pw.pw_dir is null || pw.pw_dir[0] == '\0'
74 				? "/"
75 				: pw.pw_dir, 1);
76 
77 	if(uid == getuid && gid == getgid) {
78 		argv[2].execvp(&argv[2]);
79 
80 		return 1;
81 	}
82 
83 	if(group && group[0] != '\0') {
84 		if(1.setgroups(&gid) < 0)
85 			"Error while setting group '%s': %s"
86 				.fail!errno(group, errno.strerror);
87 	} else {
88 		import core.stdc.stdlib : realloc;
89 		int ngroups;
90 		gid_t* gl;
91 
92 		while(true) {
93 			if(pw.pw_name.getgrouplist(gid, gl, &ngroups) >= 0) {
94 				if(ngroups.setgroups(gl) < 0)
95 					"Error while setting groups: %s"
96 						.fail!errno(errno.strerror);
97 				break;
98 			}
99 
100 			gl = cast(typeof(gl)) gl.realloc(ngroups * gid_t.sizeof);
101 			if(gl is null) "Out of memory".fail!(-1);
102 		}
103 
104 		if(gl !is null)
105 			gl.realloc(0);
106 	}
107 
108 	if(gid.setgid < 0)
109 		"Error while changing group: %s".fail!errno(errno.strerror);
110 
111 	if(uid.setuid < 0)
112 		"Error while changing user: %s".fail!errno(errno.strerror);
113 
114 	argv[2].execvp(&argv[2]);
115 
116 	return 1;
117 }