From b4b2164f29f3ae04ae9e3b7c0913ee8125910476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Sun, 22 Sep 2019 00:00:00 +0000 Subject: [PATCH] wget: move from cpp-cheat --- README.adoc | 6 +++ path_properties.py | 14 ++++-- userland/posix/wget.c | 99 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 userland/posix/wget.c diff --git a/README.adoc b/README.adoc index cb3ed72..8b4d7b1 100644 --- a/README.adoc +++ b/README.adoc @@ -13120,6 +13120,12 @@ The example allocates two ints and uses them, and then deallocates back. Bibliography: https://stackoverflow.com/questions/6988487/what-does-the-brk-system-call-do/31082353#31082353 +==== socket + +A bit like `read` and `write`, but from / to the Internet! + +* link:userland/posix/wget.c[] tiny `wget` re-implementation. See: https://stackoverflow.com/questions/11208299/how-to-make-an-http-get-request-in-c-without-libcurl/35680609#35680609 + === Userland multithreading The following sections are related to multithreading in userland: diff --git a/path_properties.py b/path_properties.py index 5d60a1c..82aaf85 100644 --- a/path_properties.py +++ b/path_properties.py @@ -37,6 +37,7 @@ class PathProperties: 'cc_flags_after': ['-lm', LF], 'cc_pedantic': True, 'cxx_std': default_cxx_std, + # Shuts system down, consumes a lot of memory, etc. 'disrupts_system': False, # Expected program exit status. When signals are raised, this refers # to the native exit status. as reported by Bash #?. @@ -60,15 +61,20 @@ class PathProperties: # it only generates intermediate object files. Therefore it # should not be run while testing. 'no_executable': False, - # The script requires a non-trivial argument to be passed to run properly. + # The script requires a non-trivial to determine argument to be passed to run properly. 'requires_argument': False, + # Let's not test stuff that relies on the internet by default, user might be offline, + # or Internet might be slow and make tests slow. + 'requires_internet': False, + # Requires certain of our custom kernel modules to be inserted to run. + 'requires_kernel_modules': False, + # gem5 syscall emulation cannot handle dynamically linked exectuables properly. + # https://stackoverflow.com/questions/50542222/how-to-run-a-dynamically-linked-executable-syscall-emulation-mode-se-py-in-gem5 'requires_dynamic_library': False, 'requires_m5ops': False, # gem5 fatal: syscall getcpu (#168) unimplemented. 'requires_syscall_getcpu': False, 'requires_semihosting': False, - # Requires certain of our custom kernel modules to be inserted to run. - 'requires_kernel_modules': False, # The example requires sudo, which usually implies that it can do something # deeply to the system it runs on, which would preventing further interactive # or test usage of the system, for example poweroff or messing up the GUI. @@ -168,6 +174,7 @@ class PathProperties: not self['more_than_1s'] and not self['no_executable'] and not self['requires_argument'] and + not self['requires_internet'] and not self['requires_kernel_modules'] and not self['requires_sudo'] and not self['skip_run_unclassified'] and @@ -556,6 +563,7 @@ path_properties_tuples = ( 'pthread_self.c': { 'test_run_args': {'cpus': 2}, }, + 'wget.c': {'requires_internet': True}, 'sleep_forever.c': {'more_than_1s': True}, 'virt_to_phys_test.c': {'more_than_1s': True}, } diff --git a/userland/posix/wget.c b/userland/posix/wget.c new file mode 100644 index 0000000..888b0ce --- /dev/null +++ b/userland/posix/wget.c @@ -0,0 +1,99 @@ +/* https://cirosantilli.com/linux-kernel-module-cheat#socket */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include /* getprotobyname */ +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + char buffer[BUFSIZ]; + enum CONSTEXPR { MAX_REQUEST_LEN = 1024}; + char request[MAX_REQUEST_LEN]; + char request_template[] = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n"; + struct protoent *protoent; + char *hostname = "example.com"; + in_addr_t in_addr; + int request_len; + int socket_file_descriptor; + ssize_t nbytes_total, nbytes_last; + struct hostent *hostent; + struct sockaddr_in sockaddr_in; + unsigned short server_port = 80; + + if (argc > 1) + hostname = argv[1]; + if (argc > 2) + server_port = strtoul(argv[2], NULL, 10); + + request_len = snprintf(request, MAX_REQUEST_LEN, request_template, hostname); + if (request_len >= MAX_REQUEST_LEN) { + fprintf(stderr, "request length large: %d\n", request_len); + exit(EXIT_FAILURE); + } + + /* Build the socket. */ + protoent = getprotobyname("tcp"); + if (protoent == NULL) { + perror("getprotobyname"); + exit(EXIT_FAILURE); + } + socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto); + if (socket_file_descriptor == -1) { + perror("socket"); + exit(EXIT_FAILURE); + } + + /* Build the address. */ + hostent = gethostbyname(hostname); + if (hostent == NULL) { + fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname); + exit(EXIT_FAILURE); + } + in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list))); + if (in_addr == (in_addr_t)-1) { + fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list)); + exit(EXIT_FAILURE); + } + sockaddr_in.sin_addr.s_addr = in_addr; + sockaddr_in.sin_family = AF_INET; + sockaddr_in.sin_port = htons(server_port); + + /* Actually connect. */ + if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) { + perror("connect"); + exit(EXIT_FAILURE); + } + + /* Send HTTP request. */ + nbytes_total = 0; + while (nbytes_total < request_len) { + nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total); + if (nbytes_last == -1) { + perror("write"); + exit(EXIT_FAILURE); + } + nbytes_total += nbytes_last; + } + + /* Read the response. */ + fprintf(stderr, "debug: before first read\n"); + while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) { + fprintf(stderr, "debug: after a read\n"); + write(STDOUT_FILENO, buffer, nbytes_total); + } + fprintf(stderr, "debug: after last read\n"); + if (nbytes_total == -1) { + perror("read"); + exit(EXIT_FAILURE); + } + + close(socket_file_descriptor); + exit(EXIT_SUCCESS); +}