def test_get_crontab_many_jobs(self): num_of_jobs = MANY # Create a crontab with many jobs test_jobs = create_test_tab(num_of_jobs, user1) api.set_jobs(test_jobs, user1) with open(TAB_FILE, 'r') as tabfile: crontab = tabfile.read() api.set_crontab(crontab, user1) test_crontab = api.get_crontab(user1) self.assertEqual(crontab, test_crontab)
def test_set_crontab_one_job(self): num_of_jobs = ONE # Create a crontab with one jobs test_jobs = create_test_tab(num_of_jobs, user1) api.set_jobs(test_jobs, user1) with open(TAB_FILE, 'r') as tabfile: crontab = tabfile.read() test_crontab = api.get_crontab(user1) self.assertEquals(test_crontab, False) api.set_crontab(crontab, user1) test_crontab = api.get_crontab(user1) self.assertEqual(crontab, test_crontab)
def process_edits(uid, tb_file, using_local_file, old_tab): crontab = [] jobs = [] old_jobs = api.get_jobs_for_user(uid) with open(tb_file, "r") as tab: for line in tab: line = line.strip() crontab.append(line) # Ignore newlines and full line comments if line and (line[0] != "#"): split = line.split() interval = " ".join(split[:5]) cmd = " ".join(split[5:]) try: # Ensure the crontab line is valid croniter(interval) if not cmd: raise ValueError except (KeyError, ValueError): # Otherwise prompt user to edit crontab e_str = "The crontab you entered has invalid entries, " "would you like to edit it again? (y/n) " while True: cnt = _input(e_str) if (cnt == "n") or (cnt == "N"): if using_local_file is False: os.unlink(tb_file) sys.exit(1) elif (cnt == "y") or (cnt == "Y"): return False e_str = "Please enter y or n: " # Check if job was already there job = None if line in old_tab: for old_job in old_jobs: if old_job.interval == interval and old_job.command == cmd: job = old_job old_jobs.remove(old_job) break if not job: old_tab.discard(line) job = api.Job(interval, cmd, uid, datetime.now()) jobs.append(job) if using_local_file is False: os.unlink(tb_file) api.set_crontab("\n".join(crontab), uid) api.set_jobs(jobs, uid) return True
def test_get_crontab_different_users_many_jobs(self): num_of_jobs = MANY # Create a crontab with many jobs for another user from crontab owner test_jobs = create_test_tab(num_of_jobs, user1) api.set_jobs(test_jobs, user1) with open(TAB_FILE, 'r') as tabfile: crontab = tabfile.read() api.set_crontab(crontab, user2) test_crontab = api.get_crontab(user2) self.assertEqual(crontab, test_crontab) # Append crontab for another user test_jobs = create_test_tab(num_of_jobs, user3) api.set_jobs(test_jobs, user3) with open(TAB_FILE, 'r') as tabfile: crontab = tabfile.read() api.set_crontab(crontab, user3) test_crontab = api.get_crontab(user3) self.assertEqual(crontab, test_crontab)
def main(): tb_file = None valid_crontab = None usr_euid = os.geteuid() opts = parse_args() # Convert given username into (uid, username) if opts.usr: try: opts.usr = (pwd.getpwnam(opts.usr).pw_uid, opts.usr) except KeyError: sys.exit("User '%s' does not exist." % opts.usr) # Verify permissions for selected crontab if (usr_euid != 0) and (opts.usr[0] != usr_euid): sys.exit("Access denied. You do not have permission to edit %s's " "crontab." % opts.usr[1]) # If no user is specified, set to current user (euid or uid?) else: opts.usr = (usr_euid, pwd.getpwuid(usr_euid).pw_name) # Perform rm operation if opts.rm: if opts.rm_prompt: rm = None e_str = "You are about to delete %s's crontab, continue? " "(y/n) " % opts.usr[1] while (rm != "Y") and (rm != "y"): rm = _input(e_str) if (rm == "N") or (rm == "n"): sys.exit(0) e_str = "Please enter y or n: " api.set_jobs([], opts.usr[0]) api.set_crontab(False, opts.usr[0]) sys.exit(0) while not valid_crontab: tb_file, old_tab = get_crontab(opts, valid_crontab, tb_file) valid_crontab = process_edits(opts.usr[0], tb_file, opts.file, old_tab)
def main(): tb_file = None valid_crontab = None usr_euid = os.geteuid() opts = parse_args() # Convert given username into (uid, username) if opts.usr: try: opts.usr = (pwd.getpwnam(opts.usr).pw_uid, opts.usr) except KeyError: sys.exit("User `%s' does not exist." % opts.usr) # If no user is specified, set to current user (euid or uid?) else: opts.usr = (usr_euid, pwd.getpwuid(usr_euid).pw_name) check_permissions(opts.usr, usr_euid) # Perform rm operation if opts.rm: if opts.rm_prompt: rm = None e_str = ("You are about to delete %s's crontab, continue? " "(y/n) " % opts.usr[1]) while (rm != 'Y') and (rm != 'y'): rm = _input(e_str) if (rm == 'N') or (rm == 'n'): sys.exit(0) e_str = "Please enter y or n: " api.set_jobs([], opts.usr[0]) api.set_crontab(False, opts.usr[0]) sys.exit(0) while not valid_crontab: tb_file, old_tab = get_crontab(opts, valid_crontab, tb_file) valid_crontab = process_edits(opts.usr[0], tb_file, opts.file, old_tab)
def process_edits(uid, tb_file, using_local_file, old_tab): e_str = "" jobs = [] crontab = [] old_jobs = api.get_jobs_for_user(uid) with open(tb_file, 'r') as tab: for i, line in enumerate(tab): line = line.strip() crontab.append(line) # Ignore newlines and comments if line and (line[0] != '#'): split = line.split() # Check for special interval syntax if split[0][0] == '@': try: interval = _special_intervals[split[0]] except KeyError: e_str += ("Error in line %i: Bad special interval " "syntax, %s\n" % (i + 1, split[0])) continue cmd = ' '.join(split[1:]) # Check if setting variable elif '=' in ''.join(split[:5]): name, _, value = line.partition('=') # Check if var name is zero or multiple words name = name.strip() num_names = len(name.split()) if num_names != 1: if num_names: e_str += ("Error in line %i: Bad variable " "assignment syntax; multiple variable " "names given\n" % (i + 1)) else: e_str += ("Error in line %i: Bad variable " "assignment syntax; no variable name " "given\n" % (i + 1)) else: value = value.strip() if (((value[0] == "'") or (value[0] == '"')) and (value[-1] == value[0])): # If properly wrapped in quotes, check if escaped for i, char in enumerate(reversed(value[:-1])): if char != '\\': i += 1 break if i % 2 != 0: value = value[1:-1] os.environ[name] = value continue else: # Check if five or six time/date fields if (len(split) > 5 and not any(c.isalpha() for c in split[5]) and ('*' in split[5] or any(c.isdigit() for c in split[5])) and split[5][0] != '/'): # Could print warning that using 6 time fields interval = ' '.join(split[:6]) cmd = ' '.join(split[6:]) else: interval = ' '.join(split[:5]) cmd = ' '.join(split[5:]) # Check for un-escaped %s in command job_input = None index = cmd.find('%') while (index != -1) and (cmd[index - 1] == '\\'): index = cmd.find('%', index + 1) if index != -1: job_input = _escaped_pct.sub( '%', _unescaped_pct.sub(r"\1\n", cmd[index + 1:])) cmd = cmd[:index] cmd = _escaped_pct.sub('%', cmd) # Ensure the crontab line is valid try: croniter(interval) if not cmd: raise ValueError("Missing command") except (KeyError, ValueError) as e: if isinstance(e, KeyError): e = "Bad time interval syntax, %s " % e # Replace croniter's typo-riddled error msg elif str(e) == ("Exactly 5 or 6 columns has to be " "specified for iteratorexpression."): e = ("Less than 5 fields separated by whitespace in " "the time interval (requires 5 or 6)") e_str += "Error in line %i: %s\n" % (i + 1, e) else: if not e_str: job = None # Check if job was in the old crontab if line in old_tab: for old_job in old_jobs: if (old_job.interval == interval and old_job.command == cmd and old_job.job_input == job_input and old_job.environment == os.environ): job = old_job old_jobs.remove(old_job) break if not job: old_tab.discard(line) job = api.Job(interval, cmd, uid, os.environ.copy(), job_input, datetime.now()) jobs.append(job) # Prompt user to edit crontab on error if e_str: e_str += ("The crontab you entered has invalid entries, " "would you like to edit it again? (y/n) ") while True: cnt = _input(e_str) if (cnt == 'n') or (cnt == 'N'): if using_local_file is False: os.unlink(tb_file) sys.exit(1) elif (cnt == 'y') or (cnt == 'Y'): return False e_str = "Please enter y or n: " if using_local_file is False: os.unlink(tb_file) api.set_crontab('\n'.join(crontab), uid) api.set_jobs(jobs, uid) return True
def process_edits(uid, tb_file, using_local_file, old_tab): e_str = "" jobs = [] crontab = [] old_jobs = api.get_jobs_for_user(uid) with open(tb_file, 'r') as tab: for i, line in enumerate(tab): line = line.strip() crontab.append(line) # Ignore newlines and comments if line and (line[0] != '#'): split = line.split() # Check for special interval syntax if split[0][0] == '@': try: interval = _special_intervals[split[0]] except KeyError: e_str += ("Error in line %i: Bad special interval " "syntax, %s\n" % (i + 1, split[0])) continue cmd = ' '.join(split[1:]) # Check if setting variable elif '=' in ''.join(split[:5]): name, _, value = line.partition('=') # Check if var name is zero or multiple words name = name.strip() num_names = len(name.split()) if num_names != 1: if num_names: e_str += ("Error in line %i: Bad variable " "assignment syntax; multiple variable " "names given\n" % (i + 1)) else: e_str += ("Error in line %i: Bad variable " "assignment syntax; no variable name " "given\n" % (i + 1)) else: value = value.strip() if (((value[0] == "'") or (value[0] == '"')) and (value[-1] == value[0])): # If properly wrapped in quotes, check if escaped for i, char in enumerate(reversed(value[:-1])): if char != '\\': i += 1 break if i % 2 != 0: value = value[1:-1] os.environ[name] = value continue else: # Check if five or six time/date fields if (len(split) > 5 and not any(c.isalpha() for c in split[5]) and ('*' in split[5] or any(c.isdigit() for c in split[5])) and split[5][0] != '/'): # Could print warning that using 6 time fields interval = ' '.join(split[:6]) cmd = ' '.join(split[6:]) else: interval = ' '.join(split[:5]) cmd = ' '.join(split[5:]) # Check for un-escaped %s in command job_input = None index = cmd.find('%') while (index != -1) and (cmd[index-1] == '\\'): index = cmd.find('%', index + 1) if index != -1: job_input = _escaped_pct.sub('%', _unescaped_pct.sub( r"\1\n", cmd[index+1:])) cmd = cmd[:index] cmd = _escaped_pct.sub('%', cmd) # Ensure the crontab line is valid try: croniter(interval) if not cmd: raise ValueError("Missing command") except (KeyError, ValueError) as e: if isinstance(e, KeyError): e = "Bad time interval syntax, %s " % e # Replace croniter's typo-riddled error msg elif str(e) == ("Exactly 5 or 6 columns has to be " "specified for iteratorexpression."): e = ("Less than 5 fields separated by whitespace in " "the time interval (requires 5 or 6)") e_str += "Error in line %i: %s\n" % (i + 1, e) else: if not e_str: job = None # Check if job was in the old crontab if line in old_tab: for old_job in old_jobs: if (old_job.interval == interval and old_job.command == cmd and old_job.job_input == job_input and old_job.environment == os.environ): job = old_job old_jobs.remove(old_job) break if not job: old_tab.discard(line) job = api.Job(interval, cmd, uid, os.environ.copy(), job_input, datetime.now()) jobs.append(job) # Prompt user to edit crontab on error if e_str: e_str += ("The crontab you entered has invalid entries, " "would you like to edit it again? (y/n) ") while True: cnt = _input(e_str) if (cnt == 'n') or (cnt == 'N'): if using_local_file is False: os.unlink(tb_file) sys.exit(1) elif (cnt == 'y') or (cnt == 'Y'): return False e_str = "Please enter y or n: " if using_local_file is False: os.unlink(tb_file) api.set_crontab('\n'.join(crontab), uid) api.set_jobs(jobs, uid) return True