Package config_client :: Module rhncfgcli_verify
[hide private]
[frames] | no frames]

Source Code for Module config_client.rhncfgcli_verify

  1  # 
  2  # Copyright (c) 2008--2016 Red Hat, Inc. 
  3  # 
  4  # This software is licensed to you under the GNU General Public License, 
  5  # version 2 (GPLv2). There is NO WARRANTY for this software, express or 
  6  # implied, including the implied warranties of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 
  8  # along with this software; if not, see 
  9  # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. 
 10  # 
 11  # Red Hat trademarks are not licensed under GPLv2. No permission is 
 12  # granted to use or replicate Red Hat trademarks that are incorporated 
 13  # in this software or its documentation. 
 14  # 
 15   
 16  from config_common import utils 
 17  from config_common.rhn_log import log_debug 
 18   
 19  import handler_base 
 20  import os 
 21  import stat 
 22  import pwd, grp 
 23  try: 
 24      from selinux import lgetfilecon 
 25  except: 
 26      # on rhel4 we do not support selinux 
27 - def lgetfilecon(path):
28 return [0, '']
29
30 -class Handler(handler_base.HandlerBase):
31 _usage_options = handler_base.HandlerBase._usage_options + " [ files ... ]" 32 _options_table = [ 33 handler_base.HandlerBase._option_class( 34 '--verbose', 35 "-v", 36 action="count", 37 help="Increase the amount of output detail.", 38 ), 39 handler_base.HandlerBase._option_class( 40 '--only', 41 "-o", 42 action="count", 43 help="Only show files that differ.", 44 ), 45 ] 46 47 # Main function to be run
48 - def run(self):
49 log_debug(2) 50 ret = [] 51 52 #Labels for column headers 53 status_label = "STATUS" 54 owner_status = "OWNER" 55 group_status = "GROUP" 56 mode_status = "MODE" 57 selinux_status = "SELINUX" 58 file_status = "FILE" 59 60 status_help = "(channel:local)" 61 62 maxlenarr = { 63 'status' : len(status_label), 64 'owner' : max(len(owner_status), len(status_help)), 65 'group' : max(len(group_status), len(status_help)), 66 'mode' : max(len(mode_status), len(status_help)), 67 'selinux' : max(len(selinux_status), len(status_help)), 68 } 69 70 #Iterate throught the files and process them. The src file is the file as it is in the config channel, 71 #the dst file is the file as it is in the filesystem. 72 for file in self.get_valid_files(): 73 (src, file_info, dirs_created) = self.repository.get_file_info(file) 74 75 ftype = file_info.get('filetype') 76 77 if not src: 78 continue 79 80 dst = self.get_dest_file(file) 81 82 #Added file_info parameter, which contains information needed to look for differences in the owner, group, and mode. 83 ret_dict = self._process_file(src, dst, file, ftype, file_info) 84 85 if self.options.verbose: 86 #Get the max of the return values for this file, which is used to determine the length of each field in the output. 87 #Don't include the 'file' value, because it gets displayed last in each row and will throw off the size of the other fields. 88 maxlenarr['status'] = max(maxlenarr['status'], len(ret_dict['status'])) 89 maxlenarr['owner'] = max(maxlenarr['owner'], len(ret_dict['owner'])) 90 maxlenarr['group'] = max(maxlenarr['group'], len(ret_dict['group'])) 91 maxlenarr['mode'] = max(maxlenarr['mode'], len(ret_dict['mode'])) 92 if len(ret_dict['selinux']) > 0: 93 (src, dst) = ret_dict['selinux'].split('|') 94 maxlenarr['selinux'] = max(maxlenarr['selinux'], len(src), len(dst)) 95 96 #Place the return values into a list so we can iterate through them later when we want to print them out. 97 ret.append(ret_dict) 98 99 if self.options.verbose: 100 formatstr = "%-*s" #format string for the fields where the length matters. 101 formatstr_nolimit = "%-s" #format string for the fields where the length of the field doesn't matter. Namely, the file field. 102 103 #The overall format of the output. 104 outstring = "%(status)s %(owner)s %(group)s %(mode)s %(selinux)s %(file)s" 105 106 #Print out the column labels. 107 print(outstring % { 108 "status" : formatstr % (maxlenarr['status'], status_label), 109 "owner" : formatstr % (maxlenarr['owner'], owner_status), 110 "group" : formatstr % (maxlenarr['group'], group_status), 111 "mode" : formatstr % (maxlenarr['mode'], mode_status), 112 "selinux" : formatstr % (maxlenarr['selinux'], selinux_status), 113 "file" : formatstr_nolimit % (file_status), 114 }) 115 116 print(outstring % { 117 "status" : formatstr % (maxlenarr['status'], ""), 118 "owner" : formatstr % (maxlenarr['owner'], status_help), 119 "group" : formatstr % (maxlenarr['group'], status_help), 120 "mode" : formatstr % (maxlenarr['mode'], status_help), 121 "selinux" : formatstr % (maxlenarr['selinux'], status_help), 122 "file" : "" 123 }) 124 125 #Go through each of the dictionaries returned by self._process_file(), format their values, and print out the result. 126 for fdict in ret: 127 src_selinux = dst_selinux = "" 128 if len(fdict['selinux']) > 0: 129 (src_selinux, dst_selinux) = fdict['selinux'].split('|') 130 131 if self.options.only: 132 sum = 0 133 for key in fdict.keys(): 134 if key != 'file': 135 sum += len(fdict[key]) 136 if sum == 0: 137 continue 138 139 print(outstring % { 140 "status" : formatstr % (maxlenarr['status'], fdict['status']), 141 "owner" : formatstr % (maxlenarr['owner'], fdict['owner']), 142 "group" : formatstr % (maxlenarr['group'], fdict['group']), 143 "mode" : formatstr % (maxlenarr['mode'], fdict['mode']), 144 "selinux" : formatstr % (maxlenarr['selinux'], src_selinux), 145 "file" : formatstr_nolimit % (fdict['file']), 146 }) 147 if len(dst_selinux) > 0: 148 print(outstring % { 149 "status" : formatstr % (maxlenarr['status'], ""), 150 "owner" : formatstr % (maxlenarr['owner'], ""), 151 "group" : formatstr % (maxlenarr['group'], ""), 152 "mode" : formatstr % (maxlenarr['mode'], ""), 153 "selinux" : formatstr % (maxlenarr['selinux'], dst_selinux), 154 "file" : "", 155 }) 156 #Not verbose, so give the simple output for each file... 157 else: 158 outstring = "%*s %s" 159 maxlen = max([0] + [len(x['status']) for x in ret]) + 1 160 for fdict in ret: 161 if self.options.only and len(fdict['status']) == 0: 162 continue 163 print(outstring % (maxlen, fdict['status'], fdict['file']))
164
165 - def _process_file(self, *args):
166 owner_report = "%s:%s" 167 group_report = "%s:%s" 168 perm_report = "%s:%s" 169 selinux_report = "%s|%s" 170 171 src, dst, file, type, info = args[:5] 172 owner_status = "" 173 group_status = "" 174 perm_status = "" 175 selinux_status = "" 176 177 status = [] 178 stat_err = 0 179 #Stat the destination file 180 try: 181 dst_stat = os.lstat(dst) 182 except: 183 stat_err = 1 184 if type != 'symlink': 185 src_user = info['username'] 186 if not stat_err: 187 #check for owner differences 188 dst_uid = dst_stat[stat.ST_UID] 189 try: 190 dst_user = pwd.getpwuid(dst_uid)[0] 191 except KeyError: 192 # Orphan UID with no name,return unknown 193 dst_user = "unknown(UID %d)" % (dst_uid,) 194 else: 195 dst_user = "missing" 196 197 #owner_status gets displayed with the verbose option. 198 if src_user == dst_user: 199 owner_status = "" 200 else: 201 owner_status = owner_report % (src_user, dst_user) 202 status.append('user') 203 204 src_group = info['groupname'] 205 if not stat_err: 206 #check for group differences 207 dst_gid = dst_stat[stat.ST_GID] 208 try: 209 dst_group = grp.getgrgid(dst_gid)[0] 210 except KeyError: 211 # Orphan GID with no name,return unknown 212 dst_group = "unknown(GID %d)" % (dst_gid,) 213 else: 214 dst_group = "missing" 215 216 #group_status gets displayed with the verbose option. 217 if src_group == dst_group: 218 group_status = "" 219 else: 220 group_status = group_report % (src_group, dst_group) 221 status.append('group') 222 223 #check for permissions differences 224 src_perm = str(info['filemode']) 225 if not stat_err: 226 #The mode returned by stat is decimal, but won't match the value in file_info unless it's octal. 227 #Unfortunately, the mode in file_info looks like the octal value of the mode, except it's in decimal. 228 #The solution I came up with is to convert them both into strings, rip off the leading '0' from the 229 #mode returned by stat, use the resulting strings. It sucks, but it seems to work (for now). 230 dst_perm = str(oct(stat.S_IMODE(dst_stat[stat.ST_MODE]))) 231 else: 232 dst_perm = "missing" 233 234 #rip off the leading '0' from the mode returned by stat() 235 if dst_perm[0] == '0': 236 dst_perm = dst_perm[1:] 237 238 #perm_status gets displayed with the verbose option. 239 if src_perm == dst_perm: 240 perm_status = "" 241 else: 242 perm_status = perm_report % (src_perm, dst_perm) 243 status.append('mode') 244 245 # compare selinux contexts 246 if 'selinux_ctx' in info: 247 src_selinux = info['selinux_ctx'] 248 if src_selinux: 249 if not stat_err: 250 try: 251 dst_selinux = lgetfilecon(dst)[1] 252 except OSError: 253 dst_selinux = "" 254 if dst_selinux == None: 255 dst_selinux = "" 256 else: 257 dst_selinux = "missing" 258 259 if src_selinux == dst_selinux: 260 selinux_status = "" 261 else: 262 selinux_status = selinux_report % (src_selinux, dst_selinux) 263 status.append('selinux') 264 265 #figure out the ultimate value of status. 266 if stat_err: 267 status = ["missing"] 268 elif type == 'symlink': 269 if not os.path.islink(file): 270 status = ["missing"] 271 elif os.readlink(file) != info['symlink']: 272 status.append('target-link-modified') 273 elif type == 'directory': 274 if not os.path.isdir(file): 275 status = ["missing"] 276 277 elif not os.access(dst, os.R_OK): 278 status = ["missing"] 279 280 else: 281 src_sum = utils.sha256_file(src) 282 dst_sum = utils.sha256_file(dst) 283 if src_sum != dst_sum: 284 status.append('modified') 285 286 return { 287 "status" : ','.join(status), 288 "owner" : owner_status, 289 "group" : group_status, 290 "mode" : perm_status, 291 "selinux" : selinux_status, 292 "file" : file, 293 }
294