00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 """
00039 usage: %(prog)s [list|kill|save|halt]
00040 """
00041
00042 PKG = 'ckill'
00043 NAME = 'ckill'
00044
00045 import os, sys, string, time
00046 import re
00047 import subprocess
00048 import UserDict
00049 from optparse import OptionParser
00050 import pwd
00051
00052 class ProcessError(Exception):
00053 pass
00054
00055 def spawn(cmd):
00056 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00057
00058 stdout, stderr = p.communicate()
00059 if p.returncode != 0:
00060 raise ProcessError, "Error: %s" % stderr
00061
00062 return stdout
00063
00064 class Process:
00065 def __init__(self, host, username, uid, pid, ppid, cmd, args):
00066 self.host = host
00067 self.pid = pid
00068 self.ppid = ppid
00069 self.username = username
00070 self.uid = int(uid)
00071 self.cmd = cmd
00072 self.arg_line = args
00073 self.args = args.split()
00074
00075
00076 if self.cmd.startswith("python"):
00077 self.cmd2 = string.join(self.args[:2])
00078 else:
00079 self.cmd2 = self.args[0]
00080
00081 def display(self):
00082 print "%-6s %-6s %-6s %-12s %-6s %-20s -- %s" % (self.host, self.pid, self.ppid, self.username, self.uid, self.cmd, self.cmd2)
00083
00084 def __repr__(self):
00085 return "<Process %s %s %s %s %s %s>" % (self.host, self.pid, self.ppid, self.username, self.uid, self.cmd)
00086
00087 class ProcessList(UserDict.UserDict):
00088 def __init__(self, hosts):
00089 UserDict.UserDict.__init__(self)
00090 self.hosts = hosts
00091 self.fetch()
00092
00093 def fetch(self):
00094 self.clear()
00095
00096 for host in self.hosts:
00097 self.fetch_process_list(host)
00098
00099 def fetch_process_list(self, host):
00100 cmd = ['ps', '-e', '--no-headers', '-o', 'pid=', '-o', 'ppid=', '-o', 'user=', '-o', 'uid=', '-o', 'comm=', '-o', 'args=', ]
00101 ppid = None
00102 if host != "localhost":
00103 ppid = ['python', '-c', "'import os; print os.getppid();print'", ";"]
00104 cmd = ['ssh', host, ] + ppid + cmd
00105
00106 stdout = spawn(cmd)
00107
00108 lines = stdout.split("\n")
00109
00110 thispid = None
00111 if ppid:
00112 thispid = int(lines[0].strip())
00113
00114 for line in lines[1:]:
00115 if not line: continue
00116
00117 parts = line.split(None, 5)
00118 if len(parts) != 6: continue
00119
00120 pid = int(parts[0])
00121 ppid = int(parts[1])
00122 username = parts[2]
00123 uid = parts[3]
00124 cmd = parts[4]
00125
00126 p = Process(host, username, uid, pid, ppid, cmd, parts[5])
00127 self[pid] = p
00128
00129 if thispid:
00130
00131 p = self.get(thispid, None)
00132 while p:
00133 del self[p.pid]
00134 p = self.get(p.ppid, None)
00135
00136
00137
00138 class RosInit:
00139 def __init__(self, hosts, minuid, maxuid, whitelistfn, blacklistfn, uid, regex):
00140 self.whitelist = {}
00141 self.blacklist = {}
00142
00143 self.minuid = minuid
00144 self.maxuid = maxuid
00145 self.hosts = hosts
00146 self.uid = uid
00147 self.regex = re.compile(regex)
00148
00149 if whitelistfn:
00150 self.whitelistfn = whitelistfn
00151 self.whitelistpath, fn = os.path.split(self.whitelistfn)
00152 self.readWhitelist(self.whitelistfn)
00153
00154 if blacklistfn:
00155 self.blacklistfn = blacklistfn
00156 self.blacklistpath, fn = os.path.split(self.blacklistfn)
00157 self.readBlacklist(blacklistfn)
00158
00159 def readWhitelist(self, fn):
00160 if os.path.exists(fn):
00161 lines = open(fn).readlines()
00162 for line in lines:
00163 if line.startswith("#include"):
00164 parts = line.split(' ', 1)
00165 ifn = parts[1].strip()
00166 if ifn:
00167 ifn = os.path.join(self.whitelistpath, ifn)
00168 self.readWhitelist(ifn)
00169
00170 self.whitelist[line.strip()] = 1
00171
00172 def readBlacklist(self, fn):
00173 lines = open(fn).readlines()
00174 for line in lines:
00175 if line.startswith("#include"):
00176 parts = line.split(' ', 1)
00177 ifn = parts[1].strip()
00178 if ifn:
00179 ifn = os.path.join(self.blacklistpath, ifn)
00180 self.readBlacklist(ifn)
00181
00182 elif not line.startswith("#"):
00183 self.blacklist[line.strip()] = 1
00184
00185
00186 def kill_list(self, host):
00187 pl = ProcessList([host])
00188
00189
00190 thislist = []
00191 if host == "localhost":
00192 p = pl.get(os.getpid(), None)
00193 while p:
00194 thislist.append(p.pid)
00195 p = pl.get(p.ppid, None)
00196
00197 todo = []
00198 for p in pl.values():
00199 if p.cmd2[0] == '[' and p.cmd2[-1] == ']':
00200
00201 continue
00202 if p.host == "localhost" and p.pid in thislist:
00203
00204 continue
00205
00206 if self.blacklist:
00207 include_cmd = False
00208 for term in self.blacklist.keys():
00209 if re.search(term, p.cmd2):
00210 include_cmd = True
00211 if not include_cmd:
00212 continue
00213 else:
00214 todo.append(p)
00215 continue
00216 if p.uid < self.minuid and p.uid != self.uid:
00217
00218 continue
00219 if p.uid > self.maxuid and p.uid != self.uid:
00220
00221 continue
00222 if p.cmd in self.whitelist:
00223
00224 continue
00225 if p.cmd2 in self.whitelist:
00226
00227 continue
00228
00229
00230 if self.regex.match(p.cmd) is None and self.regex.match(p.cmd2) is None:
00231 continue
00232
00233 todo.append(p)
00234
00235 return todo
00236
00237 def kill(self, signal=15):
00238 if os.getuid() != 0:
00239 print "you need to be root to run this."
00240 return
00241
00242 for host in self.hosts:
00243 todo = self.kill_list(host)
00244 todo = map(lambda x: str(x.pid), todo)
00245
00246 if len(todo) == 0: continue
00247
00248 cmd = ['kill']
00249 cmd = cmd + ["-%s" % signal]
00250 cmd = cmd + todo
00251 if host != "localhost": cmd = ['ssh', host] + cmd
00252
00253 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00254 stdout, stderr = p.communicate()
00255 if p.returncode != 0:
00256 if stderr.find("No such process") == -1:
00257 print "Error killing processes:", host, stderr
00258
00259 def list(self):
00260 for host in self.hosts:
00261 todo = self.kill_list(host)
00262
00263 for p in todo:
00264
00265 p.display()
00266
00267 def save(self):
00268 whitelist = {}
00269 pl = ProcessList(self.hosts)
00270 for p in pl.values():
00271 if p.cmd2[0] == '[' and p.cmd2[-1] == ']':
00272
00273 continue
00274 whitelist[p.cmd2] = 1
00275
00276 whitelist = whitelist.keys()
00277 whitelist.sort()
00278 for c in whitelist:
00279 print c
00280
00281 def halt(self):
00282 for host in self.hosts:
00283 cmd = ['shutdown', '-h', 'now']
00284 if host != "localhost": cmd = ['ssh', host] + cmd
00285
00286 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00287 stdout, stderr = p.communicate()
00288 if p.returncode != 0:
00289 if stderr.find("No such process") == -1:
00290 print "Error:", host, stderr
00291
00292 def reboot(self):
00293 for host in self.hosts:
00294 cmd = ['shutdown', '-r', 'now']
00295 if host != "localhost": cmd = ['ssh', host] + cmd
00296
00297 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00298 stdout, stderr = p.communicate()
00299 if p.returncode != 0:
00300 if stderr.find("No such process") == -1:
00301 print "Error:", host, stderr
00302
00303
00304 def usage(prog):
00305 print __doc__ % vars()
00306
00307 def main(argv, stdout, environ):
00308 progname = argv[0]
00309
00310 parser = OptionParser(usage="""%prog [list|kill|save|halt|reboot]
00311
00312 'kill' will send the signal specified by --sig to all processes.
00313 """
00314
00315 )
00316
00317 parser.add_option("--blacklist", action="store", dest="blacklist", default=None, help="List of programs to always kill")
00318 parser.add_option("--whitelist", action="store", dest="whitelist", default=None, help="List of programs to never kill (Default: /etc/ckill/whitelist)")
00319 parser.add_option("--minuid", action="store", type="int", dest="minuid", default=1000, help="Never kill programs run by users below this UID (Default: 1000 -- generally considered first user)")
00320 parser.add_option("--maxuid", action="store", type="int", dest="maxuid", default=29999, help="Never kill programs run by users above this UID (Default: 29999 -- generally considered last user)")
00321 parser.add_option("--user", action="store", type="string", dest="user", default='ros', help="Explicitly include user. (Defaults to user: ros)")
00322 parser.add_option("--conf", action="store", dest="confpath", default="/etc/ckill", help="Specify an alternative conf location (Default: /etc/ckill)")
00323 parser.add_option("--sig", action="store", dest="signum", default="TERM", help="Specify the signal to send (Default: TERM)")
00324 parser.add_option("--regex", action="store", dest="regex", default=".*", help="Specify a regular expression for processes to kill.")
00325
00326
00327 (options, args) = parser.parse_args()
00328
00329 sigvals = { 'HUP':1, 'SIGHUP':1,
00330 'INT':2, 'SIGINT':2,
00331 'QUIT':3, 'SIGQUIT':3,
00332 'KILL':9, 'SIGKILL': 9,
00333 'TERM':15, 'SIGTERM':15 }
00334
00335 signum = 15
00336
00337 try:
00338 signum = int(options.signum)
00339 except ValueError:
00340 try:
00341 signum = sigvals[options.signum]
00342 except KeyError:
00343 parser.error("Unknown signal: %s"%options.signum)
00344
00345
00346 if len(args) == 0:
00347 parser.error("You must specify a command.")
00348 return
00349
00350
00351 hostfn = os.path.join(options.confpath, "hosts")
00352 hosts = []
00353 if os.path.exists(hostfn):
00354 hostlines = open(hostfn).readlines()
00355 for host in hostlines: hosts.append(host.strip())
00356 else:
00357 hosts.append('localhost')
00358
00359 if options.whitelist is None:
00360 options.whitelist = os.path.join(options.confpath, "whitelist")
00361
00362 if options.user:
00363 try:
00364 uid = pwd.getpwnam(options.user).pw_uid
00365 except KeyError:
00366
00367 uid = -1
00368
00369 ri = RosInit(hosts, options.minuid, options.maxuid, options.whitelist, options.blacklist, uid, options.regex)
00370
00371 cmd = args[0]
00372
00373 if cmd == "term":
00374 ri.kill(15)
00375 elif cmd == "kill":
00376 ri.kill(signum)
00377 elif cmd == "save":
00378 ri.save()
00379 elif cmd == "list":
00380 ri.list()
00381 elif cmd == "halt":
00382 ri.halt()
00383 elif cmd == "reboot":
00384 ri.reboot()
00385 else:
00386 parser.error('Uknown command: %s'%cmd)
00387
00388
00389 if __name__ == "__main__":
00390 main(sys.argv, sys.stdout, os.environ)