#include "stdio.h"
#include "sys/socket.h"
#include "sys/errno.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "arpa/inet.h"
#include "sys/utsname.h"
#include "sys/sysctl.h"

#define FAILURE -1
#define SUCCESS 0

// prints the operating system version information
static void print_host_details() {
    // print OS version
    char os_prod_version[100];
    size_t len = sizeof(os_prod_version);
    int ret = sysctlbyname("kern.osproductversion", os_prod_version, &len, NULL, 0);
    if (ret != 0) {
        perror("failed to get MacOS version");
    } else {
        fprintf(stderr, "MacOS version: %s\n", os_prod_version);
    }

    // print the kernel info
    struct utsname kernel_info;
    ret = uname(&kernel_info);
    if (ret != 0) {
        perror("failed to get kernel info");
    } else {
        fprintf(stderr, "Kernel version: %s\n", kernel_info.version);
    }
}

/*
 * Creates a socket() and then calls setsockopt() for the SO_LINGER option, with
 * various different values and examines the return value of that call.
 */
int main() {
	fprintf(stderr, "-----------------------\n");
	print_host_details();
	fprintf(stderr, "-----------------------\n");

	// create a socket
	int socket_fd = socket(PF_INET, SOCK_STREAM, 0);
	if (socket_fd < 0) {
		fprintf(stderr, "failed to create socket, reason=%s\n", strerror(errno));
		return FAILURE;
	}
	// setsockopt() for SO_LINGER with different values
	const int vals[] = {
		10, 42, 1024, 32767, 32768, 65535, 65536, (65536 + 42)
	};	
	for (int i = 0; i < sizeof(vals) / sizeof(int); i++) {
		const int val = vals[i];
		struct linger linger;
    	linger.l_onoff = 1;
    	linger.l_linger = val;
    	const int r = setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger));
		if (r != 0) {
			fprintf(stderr, "FAILURE - failed to set SO_LINGER to value %d, reason: %s\n", val, strerror(errno));
		} else {
			fprintf(stderr, "SUCCESS - set SO_LINGER to value %d\n", val);
		}
	}
	close(socket_fd);
	return SUCCESS;
}