Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | |
michael@0 | 3 | import argparse |
michael@0 | 4 | import collections |
michael@0 | 5 | import ConfigParser |
michael@0 | 6 | import multiprocessing |
michael@0 | 7 | import time |
michael@0 | 8 | |
michael@0 | 9 | ProcessNode = collections.namedtuple('ProcessNode', ['maxtime', 'children']) |
michael@0 | 10 | |
michael@0 | 11 | class ProcessLauncher(object): |
michael@0 | 12 | |
michael@0 | 13 | """ Create and Launch process trees specified by a '.ini' file |
michael@0 | 14 | |
michael@0 | 15 | Typical .ini file accepted by this class : |
michael@0 | 16 | |
michael@0 | 17 | [main] |
michael@0 | 18 | children=c1, 1*c2, 4*c3 |
michael@0 | 19 | maxtime=10 |
michael@0 | 20 | |
michael@0 | 21 | [c1] |
michael@0 | 22 | children= 2*c2, c3 |
michael@0 | 23 | maxtime=20 |
michael@0 | 24 | |
michael@0 | 25 | [c2] |
michael@0 | 26 | children=3*c3 |
michael@0 | 27 | maxtime=5 |
michael@0 | 28 | |
michael@0 | 29 | [c3] |
michael@0 | 30 | maxtime=3 |
michael@0 | 31 | |
michael@0 | 32 | This generates a process tree of the form: |
michael@0 | 33 | [main] |
michael@0 | 34 | |---[c1] |
michael@0 | 35 | | |---[c2] |
michael@0 | 36 | | | |---[c3] |
michael@0 | 37 | | | |---[c3] |
michael@0 | 38 | | | |---[c3] |
michael@0 | 39 | | | |
michael@0 | 40 | | |---[c2] |
michael@0 | 41 | | | |---[c3] |
michael@0 | 42 | | | |---[c3] |
michael@0 | 43 | | | |---[c3] |
michael@0 | 44 | | | |
michael@0 | 45 | | |---[c3] |
michael@0 | 46 | | |
michael@0 | 47 | |---[c2] |
michael@0 | 48 | | |---[c3] |
michael@0 | 49 | | |---[c3] |
michael@0 | 50 | | |---[c3] |
michael@0 | 51 | | |
michael@0 | 52 | |---[c3] |
michael@0 | 53 | |---[c3] |
michael@0 | 54 | |---[c3] |
michael@0 | 55 | |
michael@0 | 56 | Caveat: The section names cannot contain a '*'(asterisk) or a ','(comma) |
michael@0 | 57 | character as these are used as delimiters for parsing. |
michael@0 | 58 | """ |
michael@0 | 59 | |
michael@0 | 60 | # Unit time for processes in seconds |
michael@0 | 61 | UNIT_TIME = 1 |
michael@0 | 62 | |
michael@0 | 63 | def __init__(self, manifest, verbose=False): |
michael@0 | 64 | """ |
michael@0 | 65 | Parses the manifest and stores the information about the process tree |
michael@0 | 66 | in a format usable by the class. |
michael@0 | 67 | |
michael@0 | 68 | Raises IOError if : |
michael@0 | 69 | - The path does not exist |
michael@0 | 70 | - The file cannot be read |
michael@0 | 71 | Raises ConfigParser.*Error if: |
michael@0 | 72 | - Files does not contain section headers |
michael@0 | 73 | - File cannot be parsed because of incorrect specification |
michael@0 | 74 | |
michael@0 | 75 | :param manifest: Path to the manifest file that contains the |
michael@0 | 76 | configuration for the process tree to be launched |
michael@0 | 77 | :verbose: Print the process start and end information. |
michael@0 | 78 | Genrates a lot of output. Disabled by default. |
michael@0 | 79 | """ |
michael@0 | 80 | |
michael@0 | 81 | self.verbose=verbose |
michael@0 | 82 | |
michael@0 | 83 | # Children is a dictionary used to store information from the, |
michael@0 | 84 | # Configuration file in a more usable format. |
michael@0 | 85 | # Key : string contain the name of child process |
michael@0 | 86 | # Value : A Named tuple of the form (max_time, (list of child processes of Key)) |
michael@0 | 87 | # Where each child process is a list of type: [count to run, name of child] |
michael@0 | 88 | self.children = {} |
michael@0 | 89 | |
michael@0 | 90 | |
michael@0 | 91 | cfgparser = ConfigParser.ConfigParser() |
michael@0 | 92 | |
michael@0 | 93 | if not cfgparser.read(manifest): |
michael@0 | 94 | raise IOError('The manifest %s could not be found/opened', manifest) |
michael@0 | 95 | |
michael@0 | 96 | sections = cfgparser.sections() |
michael@0 | 97 | for section in sections: |
michael@0 | 98 | # Maxtime is a mandatory option |
michael@0 | 99 | # ConfigParser.NoOptionError is raised if maxtime does not exist |
michael@0 | 100 | if '*' in section or ',' in section: |
michael@0 | 101 | raise ConfigParser.ParsingError('%s is not a valid section name. Section names cannot contain a \'*\' or \',\'.' % section) |
michael@0 | 102 | m_time = cfgparser.get(section, 'maxtime') |
michael@0 | 103 | try: |
michael@0 | 104 | m_time = int(m_time) |
michael@0 | 105 | except ValueError: |
michael@0 | 106 | raise ValueError('Expected maxtime to be an integer, specified %s' % m_time) |
michael@0 | 107 | |
michael@0 | 108 | # No children option implies there are no further children |
michael@0 | 109 | # Leaving the children option blank is an error. |
michael@0 | 110 | try: |
michael@0 | 111 | c = cfgparser.get(section, 'children') |
michael@0 | 112 | if not c: |
michael@0 | 113 | # If children is an empty field, assume no children |
michael@0 | 114 | children = None |
michael@0 | 115 | |
michael@0 | 116 | else: |
michael@0 | 117 | # Tokenize chilren field, ignore empty strings |
michael@0 | 118 | children = [[y.strip() for y in x.strip().split('*', 1)] |
michael@0 | 119 | for x in c.split(',') if x] |
michael@0 | 120 | try: |
michael@0 | 121 | for i, child in enumerate(children): |
michael@0 | 122 | # No multiplicate factor infront of a process implies 1 |
michael@0 | 123 | if len(child) == 1: |
michael@0 | 124 | children[i] = [1, child[0]] |
michael@0 | 125 | else: |
michael@0 | 126 | children[i][0] = int(child[0]) |
michael@0 | 127 | |
michael@0 | 128 | if children[i][1] not in sections: |
michael@0 | 129 | raise ConfigParser.ParsingError('No section corresponding to child %s' % child[1]) |
michael@0 | 130 | except ValueError: |
michael@0 | 131 | raise ValueError('Expected process count to be an integer, specified %s' % child[0]) |
michael@0 | 132 | |
michael@0 | 133 | except ConfigParser.NoOptionError: |
michael@0 | 134 | children = None |
michael@0 | 135 | pn = ProcessNode(maxtime=m_time, |
michael@0 | 136 | children=children) |
michael@0 | 137 | self.children[section] = pn |
michael@0 | 138 | |
michael@0 | 139 | def run(self): |
michael@0 | 140 | """ |
michael@0 | 141 | This function launches the process tree. |
michael@0 | 142 | """ |
michael@0 | 143 | self._run('main', 0) |
michael@0 | 144 | |
michael@0 | 145 | def _run(self, proc_name, level): |
michael@0 | 146 | """ |
michael@0 | 147 | Runs the process specified by the section-name `proc_name` in the manifest file. |
michael@0 | 148 | Then makes calls to launch the child processes of `proc_name` |
michael@0 | 149 | |
michael@0 | 150 | :param proc_name: File name of the manifest as a string. |
michael@0 | 151 | :param level: Depth of the current process in the tree. |
michael@0 | 152 | """ |
michael@0 | 153 | if proc_name not in self.children.keys(): |
michael@0 | 154 | raise IOError("%s is not a valid process" % proc_name) |
michael@0 | 155 | |
michael@0 | 156 | maxtime = self.children[proc_name].maxtime |
michael@0 | 157 | if self.verbose: |
michael@0 | 158 | print "%sLaunching %s for %d*%d seconds" % (" "*level, proc_name, maxtime, self.UNIT_TIME) |
michael@0 | 159 | |
michael@0 | 160 | while self.children[proc_name].children: |
michael@0 | 161 | child = self.children[proc_name].children.pop() |
michael@0 | 162 | |
michael@0 | 163 | count, child_proc = child |
michael@0 | 164 | for i in range(count): |
michael@0 | 165 | p = multiprocessing.Process(target=self._run, args=(child[1], level+1)) |
michael@0 | 166 | p.start() |
michael@0 | 167 | |
michael@0 | 168 | self._launch(maxtime) |
michael@0 | 169 | if self.verbose: |
michael@0 | 170 | print "%sFinished %s" % (" "*level, proc_name) |
michael@0 | 171 | |
michael@0 | 172 | def _launch(self, running_time): |
michael@0 | 173 | """ |
michael@0 | 174 | Create and launch a process and idles for the time specified by |
michael@0 | 175 | `running_time` |
michael@0 | 176 | |
michael@0 | 177 | :param running_time: Running time of the process in seconds. |
michael@0 | 178 | """ |
michael@0 | 179 | elapsed_time = 0 |
michael@0 | 180 | |
michael@0 | 181 | while elapsed_time < running_time: |
michael@0 | 182 | time.sleep(self.UNIT_TIME) |
michael@0 | 183 | elapsed_time += self.UNIT_TIME |
michael@0 | 184 | |
michael@0 | 185 | if __name__ == '__main__': |
michael@0 | 186 | |
michael@0 | 187 | parser = argparse.ArgumentParser() |
michael@0 | 188 | parser.add_argument("manifest", help="Specify the configuration .ini file") |
michael@0 | 189 | args = parser.parse_args() |
michael@0 | 190 | |
michael@0 | 191 | proclaunch = ProcessLauncher(args.manifest) |
michael@0 | 192 | proclaunch.run() |