1 /**
2 Copyright: Copyright (c) 2018, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module distssh.process;
7 
8 import std.algorithm : filter, splitter, map;
9 import std.exception : collectException;
10 import std.stdio : File;
11 import std..string : fromStringz;
12 import logger = std.experimental.logger;
13 
14 import from_;
15 
16 public import distssh.process.type;
17 
18 Pid[] getShallowChildren(int parent_pid) {
19     import core.sys.posix.unistd : pid_t;
20     import std.conv : to;
21     import std.array : appender;
22     import std.file : dirEntries, SpanMode, exists;
23     import std.path : buildPath, baseName;
24 
25     auto children = appender!(Pid[])();
26 
27     foreach (const p; File(buildPath("/proc", parent_pid.to!string, "task",
28             parent_pid.to!string, "children")).readln.splitter(" ").filter!(a => a.length > 0)) {
29         try {
30             children.put(p.to!pid_t.Pid);
31         } catch (Exception e) {
32             logger.trace(e.msg).collectException;
33         }
34     }
35 
36     return children.data;
37 }
38 
39 @("shall return the immediate children of the init process")
40 unittest {
41     auto res = getShallowChildren(1);
42     //logger.trace(res);
43     assert(res.length > 0);
44 }
45 
46 /** Returns: a list of all processes with the leafs being at the back
47  */
48 Pid[] getDeepChildren(int parent_pid) {
49     import std.array : appender;
50     import std.container : DList;
51 
52     auto children = DList!Pid();
53 
54     children.insert(getShallowChildren(parent_pid));
55     auto res = appender!(Pid[])();
56 
57     while (!children.empty) {
58         const p = children.front;
59         res.put(p);
60         children.insertBack(getShallowChildren(p));
61         children.removeFront;
62     }
63 
64     return res.data;
65 }
66 
67 @("shall return a list of all children of the init process")
68 unittest {
69     auto direct = getShallowChildren(1);
70     auto deep = getDeepChildren(1);
71 
72     //logger.trace(direct);
73     //logger.trace(deep);
74 
75     assert(deep.length > direct.length);
76 }
77 
78 /** Returns: a list of the process groups in the same order as the input.
79  */
80 PidGroup[] getPidGroups(const Pid[] pids) {
81     import core.sys.posix.unistd : getpgid;
82     import std.array : array, appender;
83 
84     auto res = appender!(PidGroup[])();
85     bool[pid_t] pgroups;
86 
87     foreach (const p; pids.map!(a => getpgid(a))
88             .filter!(a => a != -1)) {
89         if (p !in pgroups) {
90             pgroups[p] = true;
91             res.put(PidGroup(p));
92         }
93     }
94 
95     return res.data;
96 }
97 
98 /** Cleanup the children processes.
99  *
100  * Create an array of children process groups.
101  * Create an array of children pids.
102  * Kill the children pids to prohibit them from spawning more processes.
103  * Kill the groups from top to bottom.
104  *
105  * Params:
106  *  parent_pid = pid of the parent to analyze and kill the children of
107  *  kill = a function that kill a process
108  *  killpg = a function that kill a process group
109  *
110  * Returns: the process group of "this".
111  */
112 pid_t cleanupProcess(KillT, KillPgT)(Pid parent_pid, KillT kill, KillPgT killpg) nothrow {
113     import core.sys.posix.unistd : getpgrp, getpid, getppid;
114 
115     const this_pid = getpid();
116     const this_gid = getpgrp();
117 
118     try {
119         auto children_groups = getDeepChildren(parent_pid).getPidGroups;
120         auto direct_children = getShallowChildren(parent_pid);
121 
122         foreach (const p; direct_children.filter!(a => a != this_pid)) {
123             kill(p);
124         }
125 
126         foreach (const p; children_groups.filter!(a => a != this_gid)) {
127             killpg(p);
128         }
129     } catch (Exception e) {
130         logger.trace(e.msg).collectException;
131     }
132 
133     return this_gid;
134 }
135 
136 @("shall return a list of all children pid groups of the init process")
137 unittest {
138     import std.conv;
139     import core.sys.posix.unistd : getpgrp, getpid, getppid;
140 
141     auto direct = getShallowChildren(1);
142     auto deep = getDeepChildren(1);
143     auto groups = getPidGroups(deep);
144 
145     //logger.trace(direct.length, direct);
146     //logger.trace(deep.length, deep);
147     //logger.trace(groups.length, groups);
148 
149     assert(groups.length < deep.length);
150 }
151 
152 auto spawnDaemon(scope const(char[])[] args, scope const char[] workDir = null) {
153     import std.process : spawnProcess, Config;
154     import std.stdio : File;
155 
156     auto devNullIn = File("/dev/null");
157     auto devNullOut = File("/dev/null", "w");
158     return spawnProcess(args, devNullIn, devNullOut, devNullOut, null, Config.detached, workDir);
159 }