0406b1859ca2f5f36a661de93b33b433f393115d
[metze/samba/wip.git] / python / samba / netcmd / processes.py
1 #   Unix SMB/CIFS implementation.
2 #   List processes (to aid debugging on systems without setproctitle)
3 #   Copyright (C) 2010-2011 Jelmer Vernooij <jelmer@samba.org>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 #
18 # Testbed for loadparm.c/params.c
19 #
20 # This module simply loads a specified configuration file and
21 # if successful, dumps it's contents to stdout. Note that the
22 # operation is performed with DEBUGLEVEL at 3.
23 #
24 # Useful for a quick 'syntax check' of a configuration file.
25 #
26
27 import samba
28 import samba.getopt as options
29 from samba.netcmd import Command, CommandError, Option
30 from samba.messaging import Messaging
31
32
33 class cmd_processes(Command):
34     """List processes (to aid debugging on systems without setproctitle)."""
35
36     synopsis = "%prog [options]"
37
38     takes_optiongroups = {
39         "sambaopts": options.SambaOptions,
40         "versionopts": options.VersionOptions
41     }
42
43     takes_options = [
44         Option("--name", type=str,
45                help="Return only processes associated with one particular name"),
46         Option("--pid", type=int,
47                help="Return only names assoicated with one particular PID"),
48     ]
49
50     takes_args = []
51
52     #
53     # Get details of the samba services currently registered in irpc
54     # The prefork process model registers names in the form:
55     #     prefork-master-<service> and prefork-worker-<service>-<instance>
56     #
57     # To allow this routine to identify pre-fork master and worker process
58     #
59     # returns a tuple (filtered, masters, workers)
60     #
61     #  filtered - is a list of services with the prefork-* removed
62     #  masters  - dictionary keyed on service name of prefork master processes
63     #  workers  - dictionary keyed on service name containing an ordered list
64     #             of worker processes.
65     def get_service_data(self, msg_ctx):
66         services = msg_ctx.irpc_all_servers()
67         filtered = []
68         masters = {}
69         workers = {}
70         for service in services:
71             for id in service.ids:
72                 if service.name.startswith("prefork-master"):
73                     ns = service.name.split("-")
74                     name = ns[2] + "_server"
75                     masters[name] = service.ids[0].pid
76                 elif service.name.startswith("prefork-worker"):
77                     ns = service.name.split("-")
78                     name = ns[2] + "_server"
79                     instance = int(ns[3])
80                     pid = service.ids[0].pid
81                     if name not in workers:
82                         workers[name] = {}
83                     workers[name][instance] = (instance, pid)
84                 else:
85                     filtered.append(service)
86         return (filtered, masters, workers)
87
88     def run(self, sambaopts, versionopts, section_name=None,
89             name=None, pid=None):
90
91         lp = sambaopts.get_loadparm()
92         logger = self.get_logger("processes")
93
94         msg_ctx = Messaging()
95
96         if name is not None:
97             try:
98                 ids = msg_ctx.irpc_servers_byname(name)
99             except KeyError:
100                 ids = []
101
102             for server_id in ids:
103                 self.outf.write("%d\n" % server_id.pid)
104         elif pid is not None:
105             names = msg_ctx.irpc_all_servers()
106             for name in names:
107                 for server_id in name.ids:
108                     if server_id.pid == int(pid):
109                         self.outf.write("%s\n" % name.name)
110         else:
111             seen = {}     # Service entries already printed, service names can
112             #               be registered multiple times against a process
113             #               but we should only display them once.
114             prefork = {}  # Services running in the prefork process model
115             #               want to ensure that the master process and workers
116             #               are grouped to together.
117             (services, masters, workers) = self.get_service_data(msg_ctx)
118             self.outf.write(" Service:                          PID\n")
119             self.outf.write("--------------------------------------\n")
120
121             for service in sorted(services, key=lambda x: x.name):
122                 if service.name in masters:
123                     # If this service is running in a pre-forked process we
124                     # want to print the master process followed by all the
125                     # worker processes
126                     pid = masters[service.name]
127                     if pid not in prefork:
128                         prefork[pid] = True
129                         self.outf.write("%-26s      %6d\n" %
130                             (service.name, pid))
131                         if service.name in workers:
132                             ws = workers[service.name]
133                             for w in ws:
134                                 (instance, pid) = ws[w]
135                                 sn = "{0}(worker {1})".format(
136                                     service.name, instance)
137                                 self.outf.write("%-26s      %6d\n" % (sn, pid))
138                 else:
139                     for server_id in service.ids:
140                         if (service.name, server_id.pid) not in seen:
141                             self.outf.write("%-26s      %6d\n"
142                                 % (service.name, server_id.pid))
143                             seen[(service.name, server_id.pid)] = True