1#!/usr/bin/python
2#
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7__author__ = 'kdlucas@chromium.org (Kelly Lucas)'
8
9import logging
10import os
11
12from autotest_lib.client.bin import utils, test
13from autotest_lib.client.common_lib import error
14
15
16class platform_OSLimits(test.test):
17    """
18    Verify os limitations are set to correct levels.
19    """
20    version = 1
21
22    def get_limit(self, key, path):
23        """
24        Find and return values held in path.
25
26        Args:
27            key: dictionary key of os limit.
28            path: pathname of file with current value.
29        Returns:
30            value found in path. If it's a number we'll convert to integer.
31        """
32
33        value = None
34        # Most files have only one value, but if there are multiple values we
35        # will handle it differently. Determine this from the key.
36
37        multivals = ['max_open', 'max_procs']
38        limits = {'max_open': 'Max open files',
39                  'max_procs': 'Max processes',
40                 }
41
42        if key in multivals:
43            output = utils.read_file(path)
44            lines = output.splitlines()
45            for line in lines:
46                if limits[key] in line:
47                    fields = line.split(limits[key])
48                    vals = fields[1].split()
49                    value = (vals[0])
50        else:
51            value = (utils.read_one_line(path))
52
53        if value == 'unlimited':
54            return value
55        else:
56            return int(value)
57
58    def run_once(self):
59        errors = set()
60
61        # Max procs, max threads, and file max are dependent upon total memory.
62        # The kernel uses a formula similar to:
63        #   MemTotal-kb / 128 = max procs
64        #   MemTotal-kb / 64 = max threads
65        #   MemTotal-kb / 10 = file_max
66        # But note that MemTotal changes at the end of initialization.
67        # The values used below for these settings should give sufficient head
68        # room for usage and kernel allocation.
69
70        ref_min = {'file_max': 50000,
71                   'kptr_restrict': 1,
72                   'max_open': 1024,
73                   'max_procs': 3000,
74                   'max_threads': 7000,
75                   'ngroups_max': 65536,
76                   'nr_open': 1048576,
77                   'pid_max': 32768,
78                   'mmap_min_addr': 65536,
79                  }
80
81        ref_equal = {'leases': 1,
82                     'panic': -1,
83                     'protected_hardlinks': 1,
84                     'protected_symlinks': 1,
85                     'ptrace_scope': 1,
86                     'randomize_va_space': 2,
87                     'sched_rt_period_us': 1000000,
88                     'sched_rt_runtime_us': 800000,
89                     'sysrq': 1,
90                     'suid-dump': 2,
91                     'tcp_syncookies': 1,
92                    }
93
94        refpath = {'file_max': '/proc/sys/fs/file-max',
95                   'leases': '/proc/sys/fs/leases-enable',
96                   'max_open': '/proc/self/limits',
97                   'max_procs': '/proc/self/limits',
98                   'max_threads': '/proc/sys/kernel/threads-max',
99                   'mmap_min_addr': '/proc/sys/vm/mmap_min_addr',
100                   'kptr_restrict': '/proc/sys/kernel/kptr_restrict',
101                   'ngroups_max': '/proc/sys/kernel/ngroups_max',
102                   'nr_open': '/proc/sys/fs/nr_open',
103                   'panic': '/proc/sys/kernel/panic',
104                   'pid_max': '/proc/sys/kernel/pid_max',
105                   'protected_hardlinks': '/proc/sys/fs/protected_hardlinks',
106                   'protected_symlinks': '/proc/sys/fs/protected_symlinks',
107                   'ptrace_scope': '/proc/sys/kernel/yama/ptrace_scope',
108                   'randomize_va_space': '/proc/sys/kernel/randomize_va_space',
109                   'sched_rt_period_us': '/proc/sys/kernel/sched_rt_period_us',
110                   'sched_rt_runtime_us': '/proc/sys/kernel/sched_rt_runtime_us',
111                   'suid-dump': '/proc/sys/fs/suid_dumpable',
112                   'sysrq': '/proc/sys/kernel/sysrq',
113                   'tcp_syncookies': '/proc/sys/net/ipv4/tcp_syncookies',
114                  }
115
116        # Adjust arch-specific values.
117        if utils.get_arch().startswith('arm'):
118            ref_min['mmap_min_addr'] = 32768;
119
120        if utils.get_arch().startswith('aarch64'):
121            ref_min['mmap_min_addr'] = 32768;
122
123        # Adjust version-specific details.
124        kernel_ver = os.uname()[2]
125        if utils.compare_versions(kernel_ver, "3.6") < 0:
126            # Prior to kernel version 3.6, Yama handled link restrictions.
127            refpath['protected_hardlinks'] = \
128                '/proc/sys/kernel/yama/protected_nonaccess_hardlinks'
129            refpath['protected_symlinks'] = \
130                '/proc/sys/kernel/yama/protected_sticky_symlinks'
131
132        # Create osvalue dictionary with the same keys as refpath.
133        osvalue = {}
134        for key in refpath:
135            osvalue[key] = None
136
137        for key in ref_min:
138            osvalue[key] = self.get_limit(key, refpath[key])
139            if osvalue[key] < ref_min[key]:
140                logging.warning('%s is %d', refpath[key], osvalue[key])
141                logging.warning('%s should be at least %d', refpath[key],
142                             ref_min[key])
143                errors.add(key)
144            else:
145                logging.info('%s is %d >= %d', refpath[key], osvalue[key],
146                                               ref_min[key])
147
148        for key in ref_equal:
149            osvalue[key] = self.get_limit(key, refpath[key])
150            if osvalue[key] != ref_equal[key]:
151                logging.warning('%s is set to %d', refpath[key], osvalue[key])
152                logging.warning('Expected %d', ref_equal[key])
153                errors.add(key)
154            else:
155                logging.info('%s is %d', refpath[key], osvalue[key])
156
157        # Look for anything from refpath that wasn't checked yet:
158        for key in osvalue:
159            if osvalue[key] == None:
160                logging.warning('%s was never checked', key)
161                errors.add(key)
162
163        # If self.error is not zero, there were errors.
164        if len(errors) > 0:
165            raise error.TestFail('Found incorrect values: %s' %
166                                 ', '.join(errors))
167