From 5c1efef4551e48edaee6b0ae2da00a9f85fd3ae4 Mon Sep 17 00:00:00 2001 From: Timotej Lazar Date: Sat, 2 Mar 2019 13:25:35 +0100 Subject: Add task compile-run Edit a file, compile it, write a script to run it and store stdout and stderr. --- tasks/compile-run/task.py | 252 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 tasks/compile-run/task.py diff --git a/tasks/compile-run/task.py b/tasks/compile-run/task.py new file mode 100644 index 0000000..0f8edee --- /dev/null +++ b/tasks/compile-run/task.py @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later + +# TODO: +# - check if everything is filled in (computers, params, preparation) +# - improve scoring +# - test +# - switch to a real SSH/SFTP client to properly handle filenames + +instructions = { + 'si': '''\ +

+Postavite dva navidezna računalnika - arbiter in student. Drugi mrežni vmesnik na arbiterju in edini vmesnik na studentu naj bosta povezana v isto lokalno omrežje. + +

+Na računalnik student se prijavite z uporabniškim imenom student in geslom vaje. + +

+Obstoječi datoteko {{P_c}} v programskem jeziku C prevedite v program z imenom {{P_executable}}. Izvorna koda je namenoma pokvarjena tako, da so vanjo vrinjeni nepotrebni znaki. Pred prevajanjem jo morate popraviti. + +

+Napišite skripto {{P_script}}, ki požene {{P_executable}} z argumentom + +

"{{arg_c}}"
+ +

+ in + +

+ +

+Poskrbite, da se bo dalo skripto pognati neposredno, torej z ukazom ./{{P_script}}. Vse datoteke naj so v domačem imeniku uporabnika student, ki naj bo tudi njihov lastnik. +''', + 'en': '''\ +

+Set up two virtual machines - arbiter and student. Connect arbiter’s second network interface and student’s first interface to the same local network. + +

Log in to student as the user student with password vaje. + +

Compile the existing C file {{P_c}} into a program named {{P_executable}}. The source code was intentionally corrupted by inserting extraneous characters. Before compiling you have to fix it. + +

+Write a script called {{P_script}} that runs {{P_executable}} with the argument + +

"{{arg_c}}"
+ +and + + + +

+Ensure that the script can be run directly, ie. ./{{P_script}}. All files should be owned by student and placed in that user’s home directory. +''', +} + +computers = { + 'arbiter': { + 'disks': [{'name': 'dhcp-gw'}], + 'flavor': 'm1.tiny', + 'network_interfaces': [{'network': 'test-net'}, {'network': 'net1'}], + 'config_drive': True, + 'userdata': {'string': "#!/bin/bash\nsed -i '/cloud/d' /etc/fstab\npoweroff &\n"} + }, + 'student': { + 'disks': [{ 'name': 'console-big'}], + 'flavor': 'm1.tiny', + 'network_interfaces': [{'network': 'net1'}], + 'config_drive': True, + 'userdata': {'string': "#!/bin/bash\nsed -i '/cloud/d' /etc/fstab\npoweroff &\n"} + } +} + +networks = { 'test-net': {'public': True}, 'net1': {'public': False} } + +params_meta = { + 'student_IP': { + 'descriptions': { 'si': 'IP naslov računalnika student', 'en': 'IP address of student', + }, 'w': True, 'public': True, 'type': 'IP', 'generated': False, + }, + 'P_c': { + 'descriptions': { 'si': 'Datoteka s programom v C', 'en': 'Filename of the program in C', + }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, + }, + 'P_executable': { 'descriptions': { 'si': 'Ime prevedenega programa v C', 'en': 'Filename of the compiled C program' + }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, + }, + 'arg_c': { + 'descriptions': { 'si': 'Vrednost argumenta', 'en': 'Argument value', + }, 'w': False, 'public': True, 'type': 'short_text', 'generated': True, + }, + 'env_c': { + 'descriptions': { 'si': 'Ime okoljske spremenljivke', 'en': 'The name of the environment environment', + }, 'w': False, 'public': True, 'type': 'short_text', 'generated': True, + }, + 'out_stderr_c': { + 'descriptions': { 'si': 'Datoteka z napakami', 'en': 'File to store errors', + }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, + }, + 'P_script': { + 'descriptions': { 'si': 'Ime skripte', 'en': 'Filename of the script', + }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, + }, + 'out_stdout_c': { + 'descriptions': { 'si': 'Datoteka z izhodom', 'en': 'File to store the output', + }, 'w': False, 'public': True, 'type': 'filename', 'generated': True, + }, + 'param_gen_seed': { + 'descriptions': { 'si': 'Nakljucno seme', 'en': 'Random seed', + }, 'w': False, 'public': True, 'type': None, 'generated': True, + }, + 'c_destroy_gen_seed': { + 'descriptions': { 'si': 'Nakljucno seme za kvarjenje kode v C', 'en': 'Random seed for destroying the C code', + }, 'w': False, 'public': False, 'type': None, 'generated': True, + } + +} + +def task(student_IP, + P_c, P_executable, arg_c, env_c, out_stderr_c, out_stdout_c, P_script, + param_gen_seed): + import random + + r = random.Random(int(param_gen_seed)) + env_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(11)]) + arg_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(13)]) + stdin_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(17)]) + + return kpov_util.ssh_test(student_IP, 'student', 'vaje', ( + ('script_ls', 'ls -l ./{}'.format(P_script)), + ('executable_ls', 'ls -l ./{}'.format(P_executable)), + ('script_run', 'export {}={}; ./{}'.format(env_c, env_val, P_script)), + ('script_stderr', 'cat {}'.format(out_stderr_c)), + ('script_stdout', 'cat {}'.format(out_stdout_c)), + ('prog_stdout', 'echo "{}" | ./{} "{}" 2> /dev/null'.format(stdin_val, P_executable, arg_val)), + ('prog_stderr', 'echo "{}" | ./{} "{}" > /dev/null'.format(stdin_val, P_executable, arg_val)), + )) + +def gen_params(user_id, params_meta): + import random + r = random.Random(user_id+'evil cornholio') + params = kpov_util.default_gen(user_id, params_meta) + params['env_c'] = "".join([r.choice('ABCDEFGHIJKLMNPRSTUVZ') for i in range(5)]) + params['P_c'] = "".join([r.choice('abcdefghijklmnoprst') for i in range(5)]) + ".c" + params['param_gen_seed'] = str(r.randint(0, 2**24)) + params['c_destroy_gen_seed'] = str(r.randint(0, 2**24)) + return params + +def task_check(results, params): + def test_out_gen(arg, env): + s_out = "" + s_err = "" + for i in range(30): + s = '{}{}\n'.format( + ('XYZZY' if i%5 == 0 else ''), + (env if i%2 == 0 else arg)) + if i%3 == 0: + s_err += s + else: + s_out += s + return (s_out.strip(), s_err.strip(), len(env)) + + score = 0 + hints = [] + if results['ssh'] is not True: + hints += ['ssh failed: ' + results['ssh']] + return score, hints + + r = random.Random(int(params['param_gen_seed'])) + env_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(11)]) + arg_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(13)]) + stdin_val = "".join([r.choice('ABCDEFGHJKLMNPRSTUVZ012345') for i in range(17)]) + expected_script_stdout, expected_script_stderr, rval = test_out_gen( + params['arg_c'], env_val) + if expected_script_stderr != results['script_stderr']: + hints += ['wrong script stderr'] + else: + score += 2 + split_stdout = expected_script_stdout.split('\n') + expected_script_stdout = '\n'.join([ i for i in split_stdout if i.find('XYZZY') >= 0]) + if expected_script_stdout != results['script_stdout']: + hints += ['wrong script stdout'] + else: + score += 2 + + expected_prog_stdout, expected_prog_stderr, rval = test_out_gen(arg_val, stdin_val) + if expected_prog_stderr != results['prog_stderr']: + hints += ['wrong program stderr'] + else: + score += 2 + if expected_prog_stdout != results['prog_stdout']: + hints += ['wrong program stdout'] + else: + score += 2 + if results['script_ls'].find('-r') < 0: + hints += ['script not found'] + else: + score += 1 + if results['executable_ls'].find('xr') < 0: + hints += ['C executable not found'] + else: + score += 1 + return score, hints + +def prepare_disks(templates, task_params, global_params): + c_source = '''#include +#include +#include + +/* Odstranite vse crke Q, W, R in program se bo prevedel. */ +/* Remove all letters Q, W, R and the program will compile. */ + +int main(int argc, char **argv){ + unsigned char *arg = argv[1]; + unsigned char env[255]; + scanf("%s", env); + size_t env_len = strlen(env); + + for (int i = 0; i < 30; i++) { + fprintf(i%3 == 0 ? stderr : stdout, "%s%s\\n", + i%5 == 0 ? "XYZZY" : "", + i%2 == 0 ? env : arg); + } + return env_len; +} +''' + + import random + d = templates['console-big'] + r = random.Random(task_params['c_destroy_gen_seed']) + destroyed_c_source = c_source[:110] + for c in c_source[110:]: + i = r.randint(0, 5) + if i == 1: + destroyed_c_source += 'QW' + if i == 2: + destroyed_c_source += 'RW' + if i == 3: + destroyed_c_source += 'QR' + destroyed_c_source += c + + path_c = '/home/student/{}'.format(task_params['P_c']) + d.write(path_c, destroyed_c_source) + d.chown(1000, 1000, path_c) + + write_default_config(templates['dhcp-gw'], global_params) -- cgit v1.2.1