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 }