wget: move from cpp-cheat

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-09-22 00:00:00 +00:00
parent f7734aecf7
commit b4b2164f29
3 changed files with 116 additions and 3 deletions

View File

@@ -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:

View File

@@ -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},
}

99
userland/posix/wget.c Normal file
View File

@@ -0,0 +1,99 @@
/* https://cirosantilli.com/linux-kernel-module-cheat#socket */
#define _XOPEN_SOURCE 700
#include <arpa/inet.h>
#include <assert.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
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);
}