diff --git a/.gitignore b/.gitignore index 317347c..183fd37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ *.o *.elf -sh_elf.c +*.elf.inc + diff --git a/Makefile b/Makefile index 14209c5..2c24786 100644 --- a/Makefile +++ b/Makefile @@ -26,12 +26,23 @@ endif CFLAGS := -Wall LDADD := -lSceLibcInternal -lkernel_sys +SUBDIRS := bundles/core bundles/http2_get + +TOPTARGETS := all clean + +$(TOPTARGETS): $(SUBDIRS) + +$(SUBDIRS): + make -C $@ $(MAKECMDGOALS) + +.PHONY: $(TOPTARGETS) $(SUBDIRS) + all: shsrv.elf sh.elf -shsrv.o: sh_elf.c +shsrv.o: sh.elf.inc -bundle.o: commands/env_elf.c commands/ls_elf.c commands/ps_elf.c +builtin.o: bundles/core/core.elf.inc bundles/http2_get/http2_get.elf.inc %.o: %.c $(CC) -c $(CFLAGS) -o $@ $< @@ -39,10 +50,10 @@ bundle.o: commands/env_elf.c commands/ls_elf.c commands/ps_elf.c shsrv.elf: shsrv.o elfldr.o pt.o $(LD) -o $@ $^ $(LDADD) -sh.elf: sh.o builtin.o bundle.o elfldr.o pt.o +sh.elf: sh.o builtin.o elfldr.o pt.o $(LD) -o $@ $^ $(LDADD) -sh_elf.c: sh.elf +sh.elf.inc: sh.elf xxd -i $^ > $@ clean: @@ -50,3 +61,4 @@ clean: test: shsrv.elf nc -q0 $(PS5_HOST) $(PS5_PORT) < $^ + diff --git a/README.md b/README.md index e1bb5c8..59a4647 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ PATH enviroment variable, which is initialized to /data/hbroot/bin and /mnt/usb0/hbroot/bin ```console -john@localhost:tmp$ wget https://github.com/john-tornblom/ps5-payload-sdk/releases/download/releases%2Fv0.6/Payload.binaries.zip +john@localhost:tmp$ wget https://github.com/john-tornblom/ps5-payload-sdk/releases/download/releases%2Fv0.9/Payload.binaries.zip john@localhost:tmp$ unzip Payload.binaries.zip samples/hello_sprx/hello_sprx.elf john@localhost:tmp$ curl -T samples/hello_sprx/hello_sprx.elf ftp://ps5:2121/data/hbroot/bin/ john@localhost:tmp$ echo "hello_sprx.elf" | nc -q0 $PS5_HOST 2323 diff --git a/builtin.c b/builtin.c index e6fa044..3b39ccb 100644 --- a/builtin.c +++ b/builtin.c @@ -24,11 +24,13 @@ along with this program; see the file COPYING. If not, see #include #include - #include #include "builtin.h" +#include "bundles/core/core.elf.inc" +#include "bundles/http2_get/http2_get.elf.inc" + #define ispathsep(ch) ((ch) == '/' || (ch) == '\\') #define iseos(ch) ((ch) == '\0') @@ -38,10 +40,22 @@ along with this program; see the file COPYING. If not, see /** * Map names of builtin commands. **/ -typedef struct builtin_map { +typedef struct builtin_cmd_map { const char *name; builtin_cmd_t *cmd; -} builtin_map_t; +} builtin_cmd_map_t; + + +/** + * Map names of builtin ELFs. + **/ +typedef struct builtin_elf_map { + const char *name; + unsigned char *elf; +} builtin_elf_map_t; + + +static int main_help(int argc, char **argv); /** @@ -245,7 +259,7 @@ main_export(int argc, char **argv) { char *name; char *val; char *sep; - + if(argc < 2 || !(sep=strstr(argv[1], "="))) { printf("usage: %s NAME=value\n", argv[0]); return EXIT_FAILURE; @@ -259,7 +273,7 @@ main_export(int argc, char **argv) { perror(argv[0]); return EXIT_FAILURE; } - + return EXIT_SUCCESS; } @@ -270,9 +284,10 @@ main_export(int argc, char **argv) { static int main_exec(int argc, char** argv) { if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); return EXIT_FAILURE; } - + argv[argc] = NULL; execvp(argv[1], (char **) argv + 1); perror(argv[1]); @@ -281,25 +296,136 @@ main_exec(int argc, char** argv) { } +/** + * + **/ +static int +main_sleep(int argc, char **argv) { + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + unsigned int seconds = atoi(argv[1]); + sleep(seconds); + + return 0; +} + + /** * Lookup table for builtin commands. **/ -static builtin_map_t map[] = { +static builtin_cmd_map_t cmd_map[] = { {"cd", main_cd}, {"chroot", main_chroot}, {"exec", main_exec}, {"exit", main_exit}, {"export", main_export}, + {"help", main_help}, + {"sleep", main_sleep}, +}; + + +/** + * Lookup table for builtin ELFs. + **/ +static builtin_elf_map_t elf_map[] = { + {"cat", core_elf}, + {"chgrp", core_elf}, + {"chmod", core_elf}, + {"chown", core_elf}, + {"cmp", core_elf}, + {"cp", core_elf}, + {"echo", core_elf}, + {"env", core_elf}, + {"file", core_elf}, + {"find", core_elf}, + {"grep", core_elf}, + {"hexdump", core_elf}, + {"id", core_elf}, + {"kill", core_elf}, + {"ln", core_elf}, + {"ls", core_elf}, + {"mkdir", core_elf}, + {"mknod", core_elf}, + {"mount", core_elf}, + {"mv", core_elf}, + {"notify", core_elf}, + {"ps", core_elf}, + {"pwd", core_elf}, + {"rm", core_elf}, + {"rmdir", core_elf}, + {"sfocreate", core_elf}, + {"sfoinfo", core_elf}, + {"stat", core_elf}, + {"sum", core_elf}, + {"sync", core_elf}, + {"sysctl", core_elf}, + {"touch", core_elf}, + {"umount", core_elf}, + + {"http2_get", http2_get_elf}, }; +static int +qsort_cmp_names(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + + +/** + * Print a list of available commands to stdout. + **/ +static int +main_help(int argc, char **argv) { + size_t cmd_map_len = (sizeof(cmd_map)/sizeof(cmd_map[0])); + size_t elf_map_len = (sizeof(elf_map)/sizeof(elf_map[0])); + size_t n = cmd_map_len + elf_map_len; + const char* names[n]; + + for(size_t i=0; i + /** * Prototype for builtin commands. @@ -28,3 +30,8 @@ typedef int (builtin_cmd_t)(int argc, char **argv); **/ builtin_cmd_t* builtin_find_cmd(const char* name); + +/** + * Find a builtin ELF by its name. + **/ +uint8_t* builtin_find_elf(const char* name); diff --git a/commands/Makefile b/bundles/core/Makefile similarity index 87% rename from commands/Makefile rename to bundles/core/Makefile index 6da0f40..0fe3048 100644 --- a/commands/Makefile +++ b/bundles/core/Makefile @@ -23,20 +23,21 @@ endif CFLAGS += -Wall LDADD += -lkernel_sys -SRCS = env_elf.c ls_elf.c ps_elf.c +SRCS := $(wildcard *.c) +OBJS := $(SRCS:.c=.o) -all: $(SRCS) +all: core.elf.inc -%.o: %.c - $(CC) -c $(CFLAGS) -o $@ $< +core.elf.inc: core.elf + xxd -i $^ > $@ -%.elf: %.o +core.elf: $(OBJS) $(LD) -o $@ $^ $(LDADD) -%_elf.c: %.elf - xxd -i $^ > $@ - clean: - rm -f *.o *.elf *_elf.c + rm -f *.o *.elf *.elf.c +%.o: %.c + $(CC) -c $(CFLAGS) -o $@ $< +test: diff --git a/bundles/core/_common.c b/bundles/core/_common.c new file mode 100644 index 0000000..f12ea08 --- /dev/null +++ b/bundles/core/_common.c @@ -0,0 +1,198 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * Convenient macros. + **/ +#define ispathsep(ch) ((ch) == '/' || (ch) == '\\') +#define iseos(ch) ((ch) == '\0') +#define ispathend(ch) (ispathsep(ch) || iseos(ch)) + + +/** + * A sequence of commands. + **/ +typedef struct command_seq { + const char *name; + main_t *main; + struct command_seq *next; +} command_seq_t; + + +/** + * Head of the command sequence. + **/ +static command_seq_t* g_head = 0; + + +void +command_define(const char *name, main_t *main) { + command_seq_t* cs = malloc(sizeof(command_seq_t)); + cs->name = name; + cs->main = main; + cs->next = g_head; + g_head = cs; +} + + +main_t* +command_find(const char *name) { + for(command_seq_t* cs=g_head; cs; cs=cs->next) { + if(!strcmp(name, cs->name)) { + return cs->main; + } + } + return 0; +} + + +char* +get_workdir(void) { + return getenv("PWD"); +} + + +char* +normpath(const char *in, char *buf, size_t bufsize) { + char *pos[PATH_MAX]; + char **top = pos; + char *head = buf; + int isabs = ispathsep(*in); + + if(isabs && bufsize) { + *buf++ = '/'; + bufsize--; + } + + *top++ = buf; + + while(!iseos(*in)) { + while(ispathsep(*in)) { + ++in; + } + + if(iseos(*in)) { + break; + } + + if(memcmp(in, ".", 1) == 0 && ispathend(in[1])) { + ++in; + continue; + } + + if(memcmp(in, "..", 2) == 0 && ispathend(in[2])) { + in += 2; + + if(top != pos + 1) { + buf = *--top; + + } else if(isabs) { + buf = top[-1]; + + } else { + strncpy(buf, "../", bufsize); + buf += 3; + bufsize -= 3; + } + + continue; + } + + if(top - pos >= PATH_MAX) { + return NULL; + } + + *top++ = buf; + + while(!ispathend(*in) && bufsize) { + *buf++ = *in++; + bufsize--; + } + + if(ispathsep(*in) && bufsize) { + *buf++ = '/'; + bufsize--; + } + } + + *buf = '\0'; + + if(*head == '\0') { + strcpy(head, "./"); + } + + return head; +} + + +char* +abspath(const char *relpath) { + char buf[PATH_MAX]; + + if(relpath[0] == '/') { + strncpy(buf, relpath, sizeof(buf)); + } else { + snprintf(buf, sizeof(buf), "%s/%s", get_workdir(), relpath); + } + + char *ap = malloc(PATH_MAX); + return normpath(buf, ap, PATH_MAX); +} + + +void +hexdump(void *data, size_t size) { + + for(int i=0; i. */ + +#pragma once + +#include +#include + + +/** + * Prototype for main functions. + **/ +typedef int (main_t)(int argc, char **argv); + + + +/** + * Insert a command at the head of the sequence. + **/ +void command_define(const char *name, main_t *main); + + +/** + * Find the command with a given name. + **/ +main_t* command_find(const char *name); + + +/** + * Return the current working directory of the calling process. + **/ +char* get_workdir(void); + + +/** + * Normalize a path. + **/ +char* normpath(const char *path, char *buf, size_t bufsize); + + +/** + * Return an absolute path. + **/ +char* abspath(const char *relpath); + + +/** + * Dump a memory region to stdout. + **/ +void hexdump(void *data, size_t size); diff --git a/bundles/core/cat.c b/bundles/core/cat.c new file mode 100644 index 0000000..4d69d3a --- /dev/null +++ b/bundles/core/cat.c @@ -0,0 +1,62 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +cat_main(int argc, char **argv) { + char buf[0x4000]; + ssize_t len; + int fd; + + if(argc < 2) { + fprintf(stderr, "usage: %s FILE [FILE ...]\n", argv[0]); + return EXIT_FAILURE; + } + + for (int i=1; i 0) { + write(STDOUT_FILENO, buf, len); + } + + close(fd); + } + + return EXIT_SUCCESS; +} + + +/** + * + **/ +__attribute__((constructor)) static void +cat_constructor(void) { + command_define("cat", cat_main); +} + diff --git a/bundles/core/chgrp.c b/bundles/core/chgrp.c new file mode 100644 index 0000000..304d687 --- /dev/null +++ b/bundles/core/chgrp.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +chgrp_main(int argc, char ** argv) { + const char *cp; + int gid; + struct stat statBuf; + int r; + + r = 0; + cp = argv[1]; + + if(isdigit(*cp)) { + gid = 0; + + while(isdigit(*cp)) + gid = gid * 10 + (*cp++ - '0'); + + if (*cp) { + fprintf(stderr, "Bad gid value\n"); + return 1; + } + } else { + fprintf(stderr, "Unknown group id\n"); + return 1; + } + + argc--; + argv++; + + while (argc-- > 1) { + argv++; + + if ((stat(*argv, &statBuf) < 0) || + (chown(*argv, statBuf.st_uid, gid) < 0)) { + perror(*argv); + r = 1; + } + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +chgrp_constructor(void) { + command_define("chgrp", chgrp_main); +} diff --git a/bundles/core/chmod.c b/bundles/core/chmod.c new file mode 100644 index 0000000..98f06c5 --- /dev/null +++ b/bundles/core/chmod.c @@ -0,0 +1,70 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include + +#include "_common.h" + + +#define is_octal(ch) (((ch) >= '0') && ((ch) <= '7')) + + +/** + * + **/ +static int +chmod_main(int argc, char** argv) { + const char *cp; + int mode; + int r; + + r = 0; + mode = 0; + cp = argv[1]; + + while(is_octal(*cp)) + mode = mode * 8 + (*cp++ - '0'); + + if(*cp) { + fprintf(stderr, "Mode must be octal\n"); + return 1; + } + + argc--; + argv++; + + while(argc-- > 1) { + if(chmod(argv[1], mode) < 0) { + perror(argv[1]); + r = 1; + } + argv++; + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +chmod_constructor(void) { + command_define("chmod", chmod_main); +} diff --git a/bundles/core/chown.c b/bundles/core/chown.c new file mode 100644 index 0000000..1356075 --- /dev/null +++ b/bundles/core/chown.c @@ -0,0 +1,81 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +chown_main(int argc, char** argv) { + const char *cp; + int uid; + struct stat statBuf; + int r; + + r = 0; + cp = argv[1]; + + if(isdigit(*cp)) { + uid = 0; + + while(isdigit(*cp)) + uid = uid * 10 + (*cp++ - '0'); + + if(*cp) { + fprintf(stderr, "Bad uid value\n"); + return 1; + } + } else { + fprintf(stderr, "Unknown user id\n"); + return 1; + } + + argc--; + argv++; + + while(argc-- > 1) { + argv++; + + if((stat(*argv, &statBuf) < 0) || + (chown(*argv, uid, statBuf.st_gid) < 0)) { + perror(*argv); + r = 1; + } + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +chown_constructor(void) { + command_define("chown", chown_main); +} + diff --git a/bundles/core/cmp.c b/bundles/core/cmp.c new file mode 100644 index 0000000..d104eb9 --- /dev/null +++ b/bundles/core/cmp.c @@ -0,0 +1,171 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +#define BUF_SIZE 8192 + + +/** + * state variable for catching user interpt signals + **/ +static int interupted = 0; + + +/** + * Catch user interupts + **/ +static void +on_SIGINT(int val) { + interupted = 1; +} + + +/** + * + **/ +static int +cmp_main(int argc, char** argv) { + int fd1; + int fd2; + int cc1; + int cc2; + long pos; + const char *bp1; + const char *bp2; + char buf1[BUF_SIZE]; + char buf2[BUF_SIZE]; + struct stat statBuf1; + struct stat statBuf2; + int r; + + r = 0; + + signal(SIGINT, on_SIGINT); + + if(stat(argv[1], &statBuf1) < 0){ + perror(argv[1]); + return 1; + } + + if(stat(argv[2], &statBuf2) < 0) { + perror(argv[2]); + return 1; + } + + if((statBuf1.st_dev == statBuf2.st_dev) && + (statBuf1.st_ino == statBuf2.st_ino)) { + printf("Files are links to each other\n"); + return 0; + } + + if(statBuf1.st_size != statBuf2.st_size) { + printf("Files are different sizes\n"); + return 1; + } + + fd1 = open(argv[1], O_RDONLY); + + if (fd1 < 0) { + perror(argv[1]); + return 1; + } + + if((fd2 = open(argv[2], O_RDONLY)) < 0) { + perror(argv[2]); + close(fd1); + return 1; + } + + pos = 0; + while(1) { + if(interupted) { + goto closefiles; + } + + if((cc1 = read(fd1, buf1, sizeof(buf1))) < 0) { + perror(argv[1]); + r = 1; + goto closefiles; + } + + if((cc2 = read(fd2, buf2, sizeof(buf2))) < 0) { + perror(argv[2]); + r = 1; + goto closefiles; + } + + if ((cc1 == 0) && (cc2 == 0)) { + printf("Files are identical\n"); + r = 0; + goto closefiles; + } + + if(cc1 < cc2) { + printf("First file is shorter than second\n"); + r = 1; + goto closefiles; + } + + if(cc1 > cc2) { + printf("Second file is shorter than first\n"); + r = 1; + goto closefiles; + } + + if(memcmp(buf1, buf2, cc1) == 0) { + pos += cc1; + continue; + } + + bp1 = buf1; + bp2 = buf2; + + while(*bp1++ == *bp2++) { + pos++; + } + printf("Files differ at byte position %ld\n", pos); + r = 1; + + goto closefiles; + } + + closefiles: + close(fd1); + close(fd2); + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +cmp_constructor(void) { + command_define("cmp", cmp_main); +} diff --git a/bundles/core/cp.c b/bundles/core/cp.c new file mode 100644 index 0000000..6fef1af --- /dev/null +++ b/bundles/core/cp.c @@ -0,0 +1,106 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +#define BUFSIZE 1024*8 + + +/** + * + **/ +static int +copy_file(const char* src, const char *dst) { + int srcfd, dstfd; + struct stat srcstat; + uint8_t buf[BUFSIZE]; + int len; + + if(stat(src, &srcstat) != 0) { + return -1; + } + + if((srcfd = open(src, O_RDONLY)) < 0) { + return -1; + } + + int flags = O_CREAT | O_WRONLY | O_TRUNC; + int mode = srcstat.st_mode; + if((dstfd = open(dst, flags, mode)) < 0) { + return -1; + } + + int rc = 0; + while((len = read(srcfd, buf, BUFSIZE)) > 0) { + if(write(dstfd, buf, len) != len) { + rc = -1; + break; + } + } + + close(srcfd); + close(dstfd); + + return rc; +} + + +/** + * + **/ +static int +cp_main(int argc, char** argv) { + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + char* dest = abspath(argv[argc-1]); + + for(int i=0; i. */ + +#include + +#include "_common.h" + + +/** + * + **/ +static int +echo_main(int argc, char **argv) { + for(int i=1; i. */ + +#include + +#include "_common.h" + + +extern char **environ; + + +/** + * + **/ +static int +env_main(int argc, char **argv) { + char **var; + + if(!environ) { + return 0; + } + + for(var=environ; *var; var++) { + fprintf(stdout, "%s\n", *var); + } + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +env_constructor(void) { + command_define("env", env_main); +} diff --git a/bundles/core/file.c b/bundles/core/file.c new file mode 100644 index 0000000..4f38e0c --- /dev/null +++ b/bundles/core/file.c @@ -0,0 +1,198 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static const char * +file_check(const char *name) { + int mode; + int fd; + int cc; + int i; + int ch; + int bad_count; + char *cp; + struct stat statbuf; + char data[8192]; + static char info[1024]; + + cp = info; + *cp = '\0'; + + if(lstat(name, &statbuf) < 0) { + if (errno == ENOENT) { + return "non-existent"; + } + sprintf(cp, "stat failed: %s", strerror(errno)); + return info; + } + + mode = statbuf.st_mode; + if(S_ISDIR(mode)) { + return "directory"; + } + + if(S_ISCHR(mode)) { + return "character device"; + } + + if(S_ISBLK(mode)) { + return "block device"; + } + + if(S_ISFIFO(mode)) { + return "named pipe"; + } + + if(S_ISLNK(mode)) { + return "symbolic link"; + } + + if(S_ISSOCK(mode)) { + return "socket"; + } + + if(!S_ISREG(mode)) { + sprintf(cp, "unknown mode 0x%x, \n", mode); + cp += strlen(cp); + } + + if((mode & (S_IEXEC | S_IXGRP | S_IXOTH)) != 0) { + strcpy(cp, "executable, "); + cp += strlen(cp); + } + + if((fd = open(name, O_RDONLY)) < 0) { + sprintf(cp, "unreadable: %s", strerror(errno)); + return info; + } + + if((cc = read(fd, data, sizeof(data))) < 0) { + sprintf(cp, "read error: %s", strerror(errno)); + close(fd); + return info; + } + + close(fd); + + + if(cc == 0) { + strcpy(cp, "empty file"); + return info; + } + + if ((cc > 2) && (data[0] == '#') && (data[1] == '!')) { + char * begin; + char * end; + + data[sizeof(data) - 1] = '\0'; + begin = &data[2]; + + while(*begin == ' ') { + begin++; + } + end = begin; + + while(*end && (*end != ' ') && (*end != '\n')) { + end++; + } + *end = '\0'; + sprintf(cp, "script for \"%s\"", begin); + return info; + } + + if((data[0] == '\037') && (data[1] == '\235')) { + return "compressed file"; + } + + if((data[0] == '\037') && (data[1] == '\213')) { + return "GZIP file"; + } + + if((data[0] == '\177') && (memcmp(&data[1], "ELF", 3) == 0)) { + strcpy(cp, "ELF program"); + return info; + } + + bad_count = 0; + for(i = 0; i < cc; i++) { + ch = data[i]; + if((ch == '\n') || (ch == '\t')) { + continue; + } + if(isspace(ch) || isprint(ch)) { + continue; + } + bad_count++; + } + + if(bad_count != 0) { + strcpy(cp, "binary"); + return info; + } + + strcpy(cp, "text file"); + return info; +} + + +/** + * + **/ +static int +file_main(int argc, char** argv) { + const char *name; + const char *info; + + argc--; + argv++; + + while(argc-- > 0) { + name = *argv++; + + if(!(info=file_check(name))) { + info = "No information available"; + } + printf("%s: %s\n", name, info); + } + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +file_constructor(void) { + command_define("file", file_main); +} diff --git a/bundles/core/find.c b/bundles/core/find.c new file mode 100644 index 0000000..6a867d9 --- /dev/null +++ b/bundles/core/find.c @@ -0,0 +1,387 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * global state variable + **/ +static int interupted = 0; +static int xdev_flag; +static dev_t xdev_device; +static long file_size; +static const char *file_pattern; +static const char *file_type; + + +/** + * Catch user interupts + **/ +static void +on_SIGINT(int val) { + interupted = 1; +} + + +static int +match(const char * text, const char * pattern) { + const char *retry_pat; + const char *retry_text; + int ch; + int found; + + retry_pat = NULL; + retry_text = NULL; + + while (*text || *pattern) { + ch = *pattern++; + + switch (ch) { + case '*': + retry_pat = pattern; + retry_text = text; + break; + + case '[': + found = 0; + + while((ch = *pattern++) != ']') { + if(ch == '\\') { + ch = *pattern++; + } + + if(ch == '\0') { + return 0; + } + + if(*text == ch) { + found = 1; + } + } + + if(!found) { + pattern = retry_pat; + text = ++retry_text; + } + + /* fall into next case */ + + case '?': + if(*text++ == '\0') { + return 0; + } + break; + + case '\\': + ch = *pattern++; + if(ch == '\0') { + return 0; + } + + /* fall into next case */ + + default: + if(*text == ch) { + if(*text) { + text++; + } + break; + } + + if(*text) { + pattern = retry_pat; + text = ++retry_text; + break; + } + return 0; + } + + if(!pattern) { + return 0; + } + + } + return 1; +} + + + +/** + * + **/ +static int +test_file(const char * full_name, const struct stat * statbuf) { + const char * cp; + const char * entry_name; + int want_type; + int mode; + + mode = statbuf->st_mode; + + if(file_type != NULL) { + want_type = 0; + for (cp = file_type; *cp; cp++) { + switch(*cp) { + case 'f': + if(S_ISREG(mode)) { + want_type = 1; + } + break; + + case 'd': + if(S_ISDIR(mode)) { + want_type = 1; + } + break; + + case 'p': + if(S_ISFIFO(mode)) { + want_type = 1; + } + break; + + case 'c': + if(S_ISCHR(mode)) { + want_type = 1; + } + break; + + case 'b': + if(S_ISBLK(mode)) { + want_type = 1; + } + break; + + case 's': + if(S_ISSOCK(mode)) { + want_type = 1; + } + break; + + case 'l': + if(S_ISLNK(mode)) { + want_type = 1; + } + break; + + default: + break; + } + } + + if(!want_type) { + return 0; + } + } + + if(file_size > 0) { + if(!S_ISREG(mode) && !S_ISDIR(mode)) { + return 0; + } + + if(statbuf->st_size < file_size) { + return 0; + } + } + + if(file_pattern) { + if((entry_name = strrchr(full_name, '/'))) { + entry_name++; + } else { + entry_name = full_name; + } + if(!match(entry_name, file_pattern)) { + return 0; + } + } + + return 1; +} + + +/* + * + */ +static void +examine_directory(const char * path) { + DIR *dir; + int need_slash; + struct dirent *entry; + struct stat statbuf; +#define MAX_NAME_SIZE (1024 * 10) + char full_name[MAX_NAME_SIZE]; + + if(!(dir = opendir(path))) { + fprintf(stderr, "Cannot read directory \"%s\": %s\n", + path, strerror(errno)); + return; + } + + need_slash = (*path && (path[strlen(path) - 1] != '/')); + while(!interupted && (entry = readdir(dir))) { + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) { + continue; + } + + strcpy(full_name, path); + if (need_slash) { + strcat(full_name, "/"); + } + strcat(full_name, entry->d_name); + + if (lstat(full_name, &statbuf) < 0) { + fprintf(stderr, "Cannot stat \"%s\": %s\n", + full_name, strerror(errno)); + continue; + } + + if (test_file(full_name, &statbuf)) { + printf("%s\n", full_name); + } + + if(S_ISDIR(statbuf.st_mode) && + (!xdev_flag || (statbuf.st_dev == xdev_device))) { + examine_directory(full_name); + } + } + + closedir(dir); +} + + + +/** + * + **/ +static int +find_main(int argc, char** argv) { + const char *cp; + const char *path; + struct stat statbuf; + + signal(SIGINT, on_SIGINT); + + argc--; + argv++; + + xdev_flag = 0; + file_type = NULL; + file_pattern = NULL; + file_size = 0; + + if((argc <= 0) || (**argv == '-')) { + fprintf(stderr, "No path specified\n"); + return 1; + } + + path = *argv++; + argc--; + + while(argc > 0) { + argc--; + cp = *argv++; + + if(strcmp(cp, "-xdev") == 0) { + xdev_flag = 1; + + } else if(strcmp(cp, "-type") == 0) { + if ((argc <= 0) || (**argv == '-')) { + fprintf(stderr, "Missing type string\n"); + return 1; + } + + argc--; + file_type = *argv++; + + } else if(strcmp(cp, "-name") == 0) { + if((argc <= 0) || (**argv == '-')) { + fprintf(stderr, "Missing file name\n"); + return 1; + } + + argc--; + file_pattern = *argv++; + } else if(strcmp(cp, "-size") == 0) { + if ((argc <= 0) || (**argv == '-')) { + fprintf(stderr, "Missing file size\n"); + return 1; + } + + argc--; + cp = *argv++; + + file_size = 0; + + while(isdigit(*cp)) { + file_size = file_size * 10 + (*cp++ - '0'); + } + + if(*cp || (file_size < 0)) { + fprintf(stderr, "Bad file size specified\n"); + return 1; + } + } else { + if(*cp != '-') { + fprintf(stderr, "Missing dash in option\n"); + } else { + fprintf(stderr, "Unknown option\n"); + } + return 1; + } + } + + if(stat(path, &statbuf) < 0) { + fprintf(stderr, "Cannot stat \"%s\": %s\n", path, strerror(errno)); + return 1; + } + + if(!S_ISDIR(statbuf.st_mode)) { + fprintf(stderr, "Path \"%s\" is not a directory\n", path); + return 1; + } + + xdev_device = statbuf.st_dev; + + if(test_file(path, &statbuf)) { + printf("%s\n", path); + } + + examine_directory(path); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +find_constructor(void) { + command_define("find", find_main); +} diff --git a/bundles/core/grep.c b/bundles/core/grep.c new file mode 100644 index 0000000..d509b38 --- /dev/null +++ b/bundles/core/grep.c @@ -0,0 +1,229 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +#define BUF_SIZE 1024*8 + +/** + * state variable for catching user interupt signals + **/ +static int interupted = 0; + + +/** + * Catch user interupts + **/ +static void +on_SIGINT(int val) { + interupted = 1; +} + + +/* + * See if the specified word is found in the specified string. + */ +static int +search(const char *string, const char *word, int ignore_case) { + const char *cp1; + const char *cp2; + int len; + int low_first; + int ch1; + int ch2; + + signal(SIGINT, on_SIGINT); + + len = strlen(word); + + if (!ignore_case) { + while (1) { + if(!(string = strchr(string, word[0]))) { + return 0; + } + if(memcmp(string, word, len) == 0) { + return 1; + } + string++; + } + } + + /* + * Here if we need to check case independence. + * Do the search by lower casing both strings. + */ + low_first = *word; + if (isupper(low_first)) { + low_first = tolower(low_first); + } + + while(1) { + while (*string && (*string != low_first) && + (!isupper(*string) || (tolower(*string) != low_first))) { + string++; + } + + if (*string == '\0') { + return 0; + } + + cp1 = string; + cp2 = word; + + do { + if (*cp2 == '\0') { + return 1; + } + + ch1 = *cp1++; + + if (isupper(ch1)) { + ch1 = tolower(ch1); + } + + ch2 = *cp2++; + + if (isupper(ch2)) { + ch2 = tolower(ch2); + } + } + while (ch1 == ch2); + string++; + } +} + + +/** + * + **/ +static int +grep_main(int argc, char** argv) { + FILE * fp; + const char* word; + const char* name; + const char* cp; + int tell_name; + int ignore_case; + int tell_line; + long line; + char buf[BUF_SIZE]; + int r; + + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return 1; + } + + r = 1; + ignore_case = 0; + tell_line = 0; + + argc--; + argv++; + + if(**argv == '-') { + argc--; + cp = *argv++; + + while(*++cp) { + switch(*cp) { + case 'i': + ignore_case = 1; + break; + + case 'n': + tell_line = 1; + break; + + default: + fprintf(stderr, "Unknown option\n"); + return 1; + } + } + } + + word = *argv++; + argc--; + + tell_name = (argc > 1); + + while(argc-- > 0) { + name = *argv++; + + if (!(fp = fopen(name, "r"))) { + perror(name); + r = 1; + continue; + } + + line = 0; + + while(fgets(buf, sizeof(buf), fp)) { + if(interupted) { + fclose(fp); + return 1; + } + + line++; + + cp = &buf[strlen(buf) - 1]; + if (*cp != '\n') { + fprintf(stderr, "%s: Line too long\n", name); + } + + if(search(buf, word, ignore_case)) { + r = 0; + if(tell_name) { + printf("%s: ", name); + } + if(tell_line) { + printf("%ld: ", line); + } + fputs(buf, stdout); + } + } + + if(ferror(fp)) { + perror(name); + } + + fclose(fp); + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +grep_constructor(void) { + command_define("grep", grep_main); +} + diff --git a/bundles/core/hexdump.c b/bundles/core/hexdump.c new file mode 100644 index 0000000..d5dcc68 --- /dev/null +++ b/bundles/core/hexdump.c @@ -0,0 +1,104 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +static int +fgetb(uint8_t *buf, size_t size, FILE *fp) { + int c; + + for(int i=0; i 1) { + char *path = abspath(argv[1]); + + if(!(fp=fopen(path, "rb"))) { + perror(argv[1]); + return -1; + } + free(path); + } + + hexdump_file(fp); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +hexdump_constructor(void) { + command_define("hexdump", hexdump_main); +} diff --git a/bundles/core/id.c b/bundles/core/id.c new file mode 100644 index 0000000..9880923 --- /dev/null +++ b/bundles/core/id.c @@ -0,0 +1,55 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +id_main(int argc, char **argv) { + uid_t egid = getegid(); + gid_t rgid = getgid(); + uid_t euid = geteuid(); + gid_t ruid = getuid(); + + printf("uid=%u gid=%u", ruid, rgid); + + if(euid != ruid) { + printf(" euid=%u", euid); + } + if (egid != rgid) { + printf(" euid=%u", egid); + } + + printf("\n"); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +id_constructor(void) { + command_define("id", id_main); +} diff --git a/bundles/core/kill.c b/bundles/core/kill.c new file mode 100644 index 0000000..32d5546 --- /dev/null +++ b/bundles/core/kill.c @@ -0,0 +1,96 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +static const char *const sigtab[] = { + "", "HUP", "INT", "QUIT", "ILL", "TRAP", "ABRT", "EMT", "FPE", "KILL", "BUS", + "SEGV", "SYS", "PIPE", "ALRM", "TERM", "URG", "STOP", "TSTP", "CONT", "CHLD", + "TTIN", "TTOU", "IO", "XCPU", "XFSZ", "VTALRM", "PROF", "WINCH", "INFO", + "USR1", "USR2" +}; + + +static int +parse_sig(const char *s) { + for(int i=1; i= argc || !ispid(argv[optind]) || !sig) { + printf("usage: %s [-s signum] \n", argv[0]); + return EXIT_FAILURE; + } + + pid = atoi(argv[optind]); + + if(kill(pid, sig)) { + perror(argv[0]); + return -1; + } + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +kill_constructor(void) { + command_define("kill", kill_main); +} + diff --git a/bundles/core/ln.c b/bundles/core/ln.c new file mode 100644 index 0000000..47bec10 --- /dev/null +++ b/bundles/core/ln.c @@ -0,0 +1,130 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static const char * +build_name(const char * dir_name, const char * file_name) { + const char *cp; + static char buf[PATH_MAX]; + + if ((dir_name == NULL) || (*dir_name == '\0')) { + return file_name; + } + + if((cp = strrchr(file_name, '/'))) { + file_name = cp + 1; + } + + strcpy(buf, dir_name); + strcat(buf, "/"); + strcat(buf, file_name); + + return buf; +} + + +static int +ln_main(int argc, char **argv) { + const char *src_name; + const char *dest_name; + const char *last_arg; + struct stat statbuf; + int is_dir; + int r; + + if(argc <= 1) { + fprintf(stderr, " missing file operand\n"); + return 1; + } + r = 0; + + // soft link + if(argv[1][0] == '-') { + if(strcmp(argv[1], "-s")) { + fprintf(stderr, "Unknown option\n"); + return 1; + } + + if (argc != 4) { + fprintf(stderr, "Wrong number of arguments for symbolic link\n"); + return 1; + } + + if(symlink(argv[2], argv[3]) < 0) { + perror(argv[3]); + return 1; + } + return 0; + } + + // hard link + last_arg = argv[argc - 1]; + is_dir = (!stat(last_arg, &statbuf) && + S_ISDIR(statbuf.st_mode)); + + if ((argc > 3) && !is_dir) { + fprintf(stderr, "%s: not a directory\n", last_arg); + return 1; + } + + while (argc-- > 2) { + src_name = *(++argv); + if(access(src_name, 0) < 0) { + perror(src_name); + r = 1; + continue; + } + + dest_name = last_arg; + + if(is_dir) { + dest_name = build_name(dest_name, src_name); + } + + if(link(src_name, dest_name) < 0) { + perror(dest_name); + r = 1; + continue; + } + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +ln_constructor(void) { + command_define("ln", ln_main); +} + diff --git a/commands/ls.c b/bundles/core/ls.c similarity index 79% rename from commands/ls.c rename to bundles/core/ls.c index 01c2579..89e45ed 100644 --- a/commands/ls.c +++ b/bundles/core/ls.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 John Törnblom +/* Copyright (C) 2021 John Törnblom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -17,13 +17,13 @@ along with this program; see the file COPYING. If not, see // Code inspired by https://github.com/landley/toybox #include -#include -#include #include +#include #include -#include - #include +#include + +#include "_common.h" /** @@ -57,26 +57,10 @@ mode_to_string(mode_t mode, char *buf) { /** - * Return an absolute path. + * **/ -static char * -abspath(const char *relpath) { - char buf[PATH_MAX+1]; - - if(relpath[0] == '/') { - strncpy(buf, relpath, PATH_MAX); - } else { - getcwd(buf, PATH_MAX); - strncat(buf, "/", PATH_MAX); - strncat(buf, relpath, PATH_MAX); - } - - return strdup(buf); -} - - -int -main(int argc, char **argv) { +static int +ls_main(int argc, char **argv) { struct stat statbuf; struct dirent *ent; char buf[PATH_MAX]; @@ -84,16 +68,16 @@ main(int argc, char **argv) { char *p; if(argc <= 1) { - p = getenv("PWD"); + p = get_workdir(); } else { p = argv[1]; } p = abspath(p); - + if(!(dir=opendir(p))) { perror(argv[0]); - return EXIT_FAILURE; + return -1; } while((ent=readdir(dir))) { @@ -102,20 +86,27 @@ main(int argc, char **argv) { perror(buf); continue; } - + mode_to_string(statbuf.st_mode, buf); fprintf(stdout, "%s %s\n", buf, ent->d_name); } free(p); - + if(closedir(dir)) { perror(argv[0]); - return EXIT_FAILURE; + return -1; } - return EXIT_SUCCESS; + return 0; } +/** + * + **/ +__attribute__((constructor)) static void +ls_constructor(void) { + command_define("ls", ls_main); +} diff --git a/bundle.h b/bundles/core/main.c similarity index 81% rename from bundle.h rename to bundles/core/main.c index 6eaa080..4607f99 100644 --- a/bundle.h +++ b/bundles/core/main.c @@ -14,10 +14,16 @@ You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ -#pragma once +#include "_common.h" -/** - * Find a bundled elf by its name. - **/ -unsigned char* bundle_find_elf(const char* name); +int +main(int argc, char** argv) { + main_t* cmd; + + if((cmd=command_find(argv[0]))) { + return cmd(argc, argv); + } + + return -1; +} diff --git a/bundles/core/mkdir.c b/bundles/core/mkdir.c new file mode 100644 index 0000000..820d179 --- /dev/null +++ b/bundles/core/mkdir.c @@ -0,0 +1,61 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +mkdir_main(int argc, char **argv) { + mode_t mode = 0777; + + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + for(int i=0; i. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +mknod_main(int argc, char** argv) { + const char *cp; + int mode; + int major; + int minor; + + mode = 0666; + + if(argc < 3) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + if(strcmp(argv[2], "b") == 0) { + mode |= S_IFBLK; + } else if (strcmp(argv[2], "c") == 0) { + mode |= S_IFCHR; + } else { + fprintf(stderr, "Bad device type\n"); + return 1; + } + + major = 0; + cp = argv[3]; + + while(isdigit(*cp)) { + major = major * 10 + *cp++ - '0'; + } + + if (*cp || (major < 0) || (major > 255)) { + fprintf(stderr, "Bad major number\n"); + return 1; + } + + minor = 0; + cp = argv[4]; + + while(isdigit(*cp)) { + minor = minor * 10 + *cp++ - '0'; + } + + if(*cp || (minor < 0) || (minor > 255)) { + fprintf(stderr, "Bad minor number\n"); + return 1; + } + + if(syscall(SYS_mknod, argv[1], mode, makedev(major, minor)) < 0) { + perror(argv[1]); + return 1; + } + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +mknod_constructor(void) { + command_define("mknod", mknod_main); +} + diff --git a/bundles/core/mount.c b/bundles/core/mount.c new file mode 100644 index 0000000..6931bc9 --- /dev/null +++ b/bundles/core/mount.c @@ -0,0 +1,254 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + + +static void +build_iovec(struct iovec **iov, int *iovlen, const char *name, const char *val) { + int i; + + if (*iovlen < 0) { + return; + } + + i = *iovlen; + *iov = realloc(*iov, sizeof(**iov) * (i + 2)); + if (*iov == NULL) { + *iovlen = -1; + return; + } + + (*iov)[i].iov_base = strdup(name); + (*iov)[i].iov_len = strlen(name) + 1; + i++; + + (*iov)[i].iov_base = val ? strdup(val) : NULL; + (*iov)[i].iov_len = val ? strlen(val) + 1 : 0; + i++; + + *iovlen = i; +} + + +static char** +split_string(char *line, char *delim) { + int bufsize = 64; + int position = 0; + char **tokens = malloc(bufsize * sizeof(char*)); + char *token, **tokens_backup; + char *state = 0; + + if(!tokens) { + return NULL; + } + + token = strtok_r(line, delim, &state); + while(token != NULL) { + tokens[position] = token; + position++; + + if(position >= bufsize) { + bufsize *= 2; + tokens_backup = tokens; + tokens = realloc(tokens, bufsize * sizeof(char*)); + if(!tokens) { + free(tokens_backup); + return NULL; + } + } + + token = strtok_r(NULL, delim, &state); + } + tokens[position] = NULL; + return tokens; +} + + +static int +mount_fs(char* fstype, char* fspath, char* device, char* options, + unsigned long flags) { + struct iovec* iov = NULL; + int iovlen = 0; + + build_iovec(&iov, &iovlen, "fstype", fstype); + build_iovec(&iov, &iovlen, "fspath", fspath); + + if(device) { + build_iovec(&iov, &iovlen, "from", device); + } + + char **opts = split_string(options, ","); + for(int i=0; opts[i]!=NULL; i++) { + char *name = opts[i]; + char *value = NULL; + char *delim = strstr(opts[i], "="); + + if(delim) { + *delim = 0; + value = delim+1; + } + + build_iovec(&iov, &iovlen, name, value); + } + free(opts); + + return syscall(SYS_nmount, iov, iovlen, flags); +} + + +int +getmntinfo(struct statfs **bufp, int mode) { + struct statfs *buf; + int nitems = 0; + int size = 0; + int size2 = 0; + + if((nitems = syscall(SYS_getfsstat, 0, 0, MNT_NOWAIT)) < 0) { + return -1; + } + + size = sizeof(struct statfs) * nitems; + + if(!(buf = malloc(size))) { + return -1; + } + + memset(buf, 0, size); + + if((size2 = syscall(SYS_getfsstat, buf, size, mode)) < 0) { + return -1; + } + + *bufp = buf; + + return nitems; +} + + +static int +print_mountpoints(void) { + struct statfs *buf; + int nitems; + + if((nitems = getmntinfo(&buf, MNT_WAIT)) < 0) { + return -1; + } + + for (int i=0; i \n", argv[0]); + exit(1); + break; + } + } + + if(optind < argc) { + device = abspath(argv[optind]); + } + + if(optind+1 < argc) { + fspath = abspath(argv[optind+1]); + } + + if(device && fspath && fstype) { + if(mount_fs(fstype, fspath, device, options, flags)) { + perror(argv[0]); + rc = -1; + } + } else { + if(print_mountpoints()) { + perror(argv[0]); + } + } + + if(fspath) { + free(fspath); + } + + if(device) { + free(device); + } + + if(fstype) { + free(fstype); + } + + if(options) { + free(options); + } + + return rc; +} + + +/** + * + **/ +__attribute__((constructor)) static void +mount_constructor(void) { + command_define("mount", mount_main); +} diff --git a/bundles/core/mv.c b/bundles/core/mv.c new file mode 100644 index 0000000..409918c --- /dev/null +++ b/bundles/core/mv.c @@ -0,0 +1,261 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +#define BUF_SIZE 1024*8 + + +/** + * state variable for catching user interupt signals + **/ +static int interupted = 0; + + +/** + * Catch user interupts + **/ +static void +on_SIGINT(int val) { + interupted = 1; +} + + +/** + * Report error in case status is -1. + **/ +static void +check_status(const char * name, int status) { + if (status == -1) { + perror(name); + } +} + + +/** + * + **/ +static int +full_write(int fd, const char * buf, int len) { + int cc; + int total; + + total = 0; + + while(len > 0) { + cc = write(fd, buf, len); + + if(cc < 0) { + return -1; + } + buf += cc; + total += cc; + len -= cc; + } + + return total; +} + + +/** + * + **/ +static const char * +build_name(const char * dir_name, const char * file_name) { + const char *cp; + static char buf[PATH_MAX]; + + if ((dir_name == NULL) || (*dir_name == '\0')) { + return file_name; + } + + if((cp = strrchr(file_name, '/'))) { + file_name = cp + 1; + } + + strcpy(buf, dir_name); + strcat(buf, "/"); + strcat(buf, file_name); + + return buf; +} + + +static int +copy_file(const char *src_name, const char *dest_name, int set_modes) { + int rfd; + int wfd; + int rcc; + char buf[BUF_SIZE]; + struct stat statbuf1; + struct stat statbuf2; + struct utimbuf times; + + if(stat(src_name, &statbuf1) < 0) { + perror(src_name); + return -1; + } + + if(stat(dest_name, &statbuf2) < 0) { + statbuf2.st_ino = -1; + statbuf2.st_dev = -1; + } + + if((statbuf1.st_dev == statbuf2.st_dev) && + (statbuf1.st_ino == statbuf2.st_ino)) { + fprintf(stderr, "Copying file \"%s\" to itself\n", src_name); + return -1; + } + + if((rfd = open(src_name, O_RDONLY)) < 0) { + perror(src_name); + return -1; + } + + if((wfd = creat(dest_name, statbuf1.st_mode)) < 0) { + perror(dest_name); + close(rfd); + return -1; + } + + while((rcc = read(rfd, buf, sizeof(buf))) > 0) { + if(interupted) { + close(rfd); + close(wfd); + return -1; + } + + if(full_write(wfd, buf, rcc) < 0) { + goto error_exit; + } + } + + if(rcc < 0) { + perror(src_name); + goto error_exit; + } + + check_status("close", close(rfd)); + if(close(wfd) < 0) { + perror(dest_name); + return -1; + } + + if(set_modes) { + check_status("chmod", chmod(dest_name, statbuf1.st_mode)); + check_status("chown", chown(dest_name, statbuf1.st_uid, statbuf1.st_gid)); + + times.actime = statbuf1.st_atime; + times.modtime = statbuf1.st_mtime; + + check_status("utime", utime(dest_name, ×)); + } + + return 0; + +error_exit: + close(rfd); + close(wfd); + + return -1; +} + + +/** + * + **/ +static int +mv_main(int argc, char** argv) { + const char * src_name; + const char * dest_name; + const char * last_arg; + struct stat statbuf; + int is_dir; + int r; + + signal(SIGINT, on_SIGINT); + + r = 0; + last_arg = argv[argc - 1]; + + is_dir = (!stat(last_arg, &statbuf) && + S_ISDIR(statbuf.st_mode)); + + if((argc > 3) && !is_dir) { + fprintf(stderr, "%s: not a directory\n", last_arg); + return 1; + } + + while(!interupted && (argc-- > 2)) { + src_name = *(++argv); + + if (access(src_name, 0) < 0) { + perror(src_name); + r = 1; + continue; + } + + dest_name = last_arg; + + if(is_dir) { + dest_name = build_name(dest_name, src_name); + } + + if(rename(src_name, dest_name) >= 0) { + continue; + } + + if (errno != EXDEV) { + perror(dest_name); + r = 1; + continue; + } + + if(!copy_file(src_name, dest_name, 1)) { + r = 1; + continue; + } + + if(unlink(src_name) < 0) { + perror(src_name); + r = 1; + } + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +mv_constructor(void) { + command_define("mv", mv_main); +} diff --git a/bundles/core/notify.c b/bundles/core/notify.c new file mode 100644 index 0000000..2f8b4d2 --- /dev/null +++ b/bundles/core/notify.c @@ -0,0 +1,56 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include + +#include "_common.h" + + +typedef struct notify_request { + char useless1[45]; + char message[3075]; +} notify_request_t; + + +void sceKernelSendNotificationRequest(int, notify_request_t*, size_t, int); + + +/** + * + **/ +static int +notify_main(int argc, char **argv) { + notify_request_t req; + + bzero(&req, sizeof req); + if(argc > 1) { + strncpy(req.message, argv[1], sizeof req.message); + } + + sceKernelSendNotificationRequest(0, &req, sizeof req, 0); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +notify_constructor(void) { + command_define("notify", notify_main); +} diff --git a/commands/ps.c b/bundles/core/ps.c similarity index 83% rename from commands/ps.c rename to bundles/core/ps.c index 09fa12d..911e4b1 100644 --- a/commands/ps.c +++ b/bundles/core/ps.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 John Törnblom +/* Copyright (C) 2023 John Törnblom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -14,7 +14,6 @@ You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ - #include #include #include @@ -26,6 +25,8 @@ along with this program; see the file COPYING. If not, see #include +#include "_common.h" + typedef struct app_info { uint32_t app_id; @@ -36,6 +37,7 @@ typedef struct app_info { int sceKernelGetAppInfo(pid_t pid, app_info_t *info); +int sceKernelDlsym(int, const char*, void*); static char *state_abbrev[] = { @@ -43,8 +45,8 @@ static char *state_abbrev[] = { }; -int -main(int argc, char** argv) { +static int +ps_main(int argc, char** argv) { int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0}; app_info_t appinfo; size_t buf_size; @@ -70,7 +72,7 @@ main(int argc, char** argv) { } printf(" PID PPID PGID SID UID AuthId" - " State Tracer AppId TitleId Command\n"); + " State AppId TitleId Command\n"); for(void *ptr=buf; ptr<(buf+buf_size);) { struct kinfo_proc *ki = (struct kinfo_proc*)ptr; ptr += ki->ki_structsize; @@ -79,10 +81,10 @@ main(int argc, char** argv) { memset(&appinfo, 0, sizeof(appinfo)); } - printf("%8u %8u %8u %8u %8u %016lx %5s %8u %08x %9s %s\n", + printf("%8u %8u %8u %8u %8u %016lx %5s %08x %9s %s\n", ki->ki_pid, ki->ki_ppid, ki->ki_pgid, ki->ki_sid, ki->ki_uid, kernel_get_ucred_authid(ki->ki_pid), - state_abbrev[(int)ki->ki_stat], ki->ki_tracer, appinfo.app_id, + state_abbrev[(int)ki->ki_stat], appinfo.app_id, appinfo.title_id, ki->ki_comm); } @@ -91,3 +93,11 @@ main(int argc, char** argv) { return 0; } + +/** + * + **/ +__attribute__((constructor)) static void +ps_constructor(void) { + command_define("ps", ps_main); +} diff --git a/bundles/core/pwd.c b/bundles/core/pwd.c new file mode 100644 index 0000000..a4f2bb4 --- /dev/null +++ b/bundles/core/pwd.c @@ -0,0 +1,45 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include + +#include "_common.h" + + +static int +pwd_main(int argc, char **argv) { + char pwd[PATH_MAX]; + pwd[0] = 0; + + if(!getcwd(pwd, sizeof pwd)) { + perror(argv[0]); + return EXIT_FAILURE; + } else { + printf("%s\n", pwd); + } + + return EXIT_SUCCESS; +} + + +__attribute__((constructor)) static void +pwd_constructor(void) { + command_define("pwd", pwd_main); +} diff --git a/bundles/core/rm.c b/bundles/core/rm.c new file mode 100644 index 0000000..4bf3587 --- /dev/null +++ b/bundles/core/rm.c @@ -0,0 +1,50 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +rm_main(int argc, char** argv) { + int r = 0; + + while (argc-- > 1) { + if (unlink(argv[1]) < 0) { + perror(argv[1]); + r = 1; + } + argv++; + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +pwd_constructor(void) { + command_define("rm", rm_main); +} diff --git a/bundle.c b/bundles/core/rmdir.c similarity index 55% rename from bundle.c rename to bundles/core/rmdir.c index 18d53c2..2b25e67 100644 --- a/bundle.c +++ b/bundles/core/rmdir.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 John Törnblom +/* Copyright (C) 2021 John Törnblom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -14,35 +14,44 @@ You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ +#include +#include #include +#include +#include +#include + +#include "_common.h" -#include "commands/env_elf.c" -#include "commands/ls_elf.c" -#include "commands/ps_elf.c" /** - * Map names of bundled commands. + * **/ -typedef struct bundle_map { - const char *name; - unsigned char *elf; -} bundle_map_t; - - -static bundle_map_t map[] = { - {"env", env_elf}, - {"ls", ls_elf}, - {"ps", ps_elf}, -}; +static int +rmdir_main(int argc, char **argv) { + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + for(int i=0; i. */ + +// Code derived from https://github.com/TheOfficialFloW/VitaShell + +#pragma once + +#include + +typedef struct sfo_header { + uint32_t magic; + uint32_t version; + uint32_t keys_offset; + uint32_t data_offset; + uint32_t count; +} __attribute__((packed)) sfo_header_t; + + +typedef struct sfo_entry { + uint16_t key_offset; + uint8_t alignment; + uint8_t type; + uint32_t val_length; + uint32_t val_size; + uint32_t val_offset; +} __attribute__((packed)) sfo_entry_t; + + +#define TYPE_BIN 0 +#define TYPE_STR 2 +#define TYPE_INT 4 + +#define MAGIC 0x46535000 + diff --git a/bundles/core/sfocreate.c b/bundles/core/sfocreate.c new file mode 100644 index 0000000..f85a63f --- /dev/null +++ b/bundles/core/sfocreate.c @@ -0,0 +1,273 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + + +#include +#include +#include +#include +#include +#include +#include + +#include "sfo.h" +#include "_common.h" + + +typedef struct keyval { + char *key; + void *val; + uint8_t type; + size_t key_size; + size_t val_size; + size_t val_length; + struct keyval *next; +} keyval_t; + + +/** + * Global state variables. + **/ +static keyval_t* keyval_store = NULL; +static size_t count = 0; +static size_t keys_size = 0; +static size_t data_size = 0; + + +/** + * Insert a key-value in the global store. + **/ +static void +keyval_store_insert(keyval_t *kv) { + keyval_t *next = keyval_store; + keyval_t *prev = NULL; + + while(next && strcmp(next->key, kv->key) < 0) { + prev = next; + next = next->next; + } + + if(prev) { + kv->next = prev->next; + prev->next = kv; + + } else { + kv->next = keyval_store; + keyval_store = kv; + } + + keys_size += kv->key_size; + data_size += kv->val_size; + count += 1; +} + + +/** + * Emit the key-value store to a file. + **/ +static void +keyval_store_emit(FILE *fp) { + size_t keys_offset = 0; + size_t data_offset = 0; + keyval_t *kv = keyval_store; + uint8_t data[data_size]; + uint8_t keys[keys_size]; + sfo_entry_t entries[count]; + sfo_header_t header = { + .magic = 0x46535000, + .version = 0x0101, + .count = count, + .keys_offset = sizeof(sfo_header_t) + sizeof(entries), + .data_offset = sizeof(sfo_header_t) + sizeof(entries) + sizeof(keys) + }; + + memset(keys, 0, keys_size); + memset(data, 0, data_size); + + for(int i=0; ikey, kv->key_size); + memcpy(data + data_offset, kv->val, kv->val_size); + + entries[i].key_offset = keys_offset; + entries[i].alignment = 4; + entries[i].type = kv->type; + entries[i].val_length = kv->val_length; + entries[i].val_size = kv->val_size; + entries[i].val_offset = data_offset; + + keys_offset += kv->key_size; + data_offset += kv->val_size; + + kv = kv->next; + } + + fwrite(&header, sizeof(sfo_header_t), 1, fp); + fwrite(entries, sizeof(sfo_entry_t), count, fp); + fwrite(keys, sizeof(uint8_t), keys_size, fp); + fwrite(data, sizeof(uint8_t), data_size, fp); +} + + +/** + * Parse a key-value integer of the format KEY=VALUE. + **/ +static int +keyval_parse_integer(char* str) { + keyval_t* kv = NULL; + char *delim = NULL; + char *key = str; + int val = 0; + + if(!(delim = strstr(str, "="))) { + return -1; + } + + *delim = 0; + val = atoi(delim + 1); + + kv = malloc(sizeof(keyval_t)); + kv->type = TYPE_INT; + + kv->key = strdup(key); + kv->key_size = strlen(key) + 1; + + kv->val_size = 4; + kv->val_length = 4; + kv->val = malloc(kv->val_size); + memcpy(kv->val, &val, kv->val_size); + + keyval_store_insert(kv); + + return 0; +} + + +/** + * Parse a key-value string of the format KEY=VALUE. + **/ +static int +keyval_parse_string(char* str, size_t size) { + keyval_t* kv = NULL; + char *delim = NULL; + char *key = str; + char *val = NULL; + + if(!(delim = strstr(str, "="))) { + return -1; + } + + *delim = 0; + val = delim + 1; + + kv = malloc(sizeof(keyval_t)); + kv->type = TYPE_STR; + + kv->key = strdup(key); + kv->key_size = strlen(kv->key) + 1; + + kv->val = malloc(size); + kv->val_size = size; + kv->val_length = strnlen(val, size) + 1; + + strncpy(kv->val, val, kv->val_size); + + keyval_store_insert(kv); + + return 0; +} + + +/** + * Display usage help text and terminate program. + **/ +static void +show_help(const char *progname) { + printf("usage: %s [-i KEY=VALUE] [-s KEY=VALUE]... param.sfo\n", + progname); + exit(1); +} + + +/** + * + **/ +static int +sfocreate_main(int argc, char **argv) { + char* filename = NULL; + FILE *fp = stdout; + char *optarg = NULL; + uint32_t size = 0; + + for(int i=1; i. */ + +// Code derived from https://github.com/TheOfficialFloW/VitaShell + +#include +#include +#include +#include +#include + +#include "sfo.h" +#include "_common.h" + + +static char* +freadstr(FILE *fp) { + int size = 64; + int pos = 0; + char *tmp; + char *buf; + char c; + + if(!(buf = calloc(size, sizeof(char)))) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + return NULL; + } + + while(1) { + if((c = fgetc(fp)) <= 0) { + buf[pos] = '\0'; + return buf; + } + + buf[pos++] = c; + if(pos >= size) { + size *= 2; + tmp = buf; + if(!(buf = realloc(buf, size))) { + fprintf(stderr, "realloc: %s\n", strerror(errno)); + free(tmp); + return NULL; + } + } + } +} + + +static int +sfoinfo_file(FILE *fp) { + sfo_header_t header; + + if(fread(&header, sizeof(sfo_header_t), 1, fp) < 1) { + return -1; + } + + if(header.magic != MAGIC) { + return -1; + } + + if(!header.count) { + return 0; + } + + sfo_entry_t entries[header.count]; + if(fread(&entries, sizeof(sfo_entry_t), header.count, fp) < 1) { + return -1; + } + + for(int i=0; ival_size+1]; + char *key; + + if(fseek(fp, header.keys_offset + e->key_offset, SEEK_SET)) { + return -1; + } + + if(!(key = freadstr(fp))) { + return -1; + } + + if(fseek(fp, header.data_offset + e->val_offset, SEEK_SET)) { + free(key); + return -1; + } + + memset(val, 0, sizeof(val)); + if(fread(val, sizeof(uint8_t), e->val_size, fp) != e->val_size) { + free(key); + return -1; + } + + printf("%s(", key); + switch(e->type) { + case TYPE_INT: + printf("0x%02x%02x%02x%02x", val[3], val[2], val[1], val[0]); + break; + + case TYPE_STR: + printf("'%s'", val); + break; + + default: + printf("%d, 0x", e->type); + for(int i=0; ival_length; i++) { + printf("%02x", val[i]); + } + break; + } + printf(")\n"); + + free(key); + } + + return 0; +} + + +/** + * + **/ +static int +sfoinfo_main(int argc, char **argv) { + FILE *fp; + + if(argc <= 1) { + fprintf(stderr, "usage: %s param.sfo\n", argv[0]); + return -1; + } + + for(int i=1; i. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +static int +stat_main(int argc, char **argv) { + struct stat statbuf; + + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + char *path = abspath(argv[1]); + + if(stat(path, &statbuf) != 0) { + perror(path); + } else { + printf("filename: %s\n", argv[1]); + printf("size: %lu\n", (unsigned long)statbuf.st_size); + printf("blocks: %lu\n", (unsigned long)statbuf.st_blocks); + printf("mode: %x\n", (unsigned)statbuf.st_mode); + printf("uid: %u\n", (unsigned)statbuf.st_uid); + printf("gid: %u\n", (unsigned)statbuf.st_gid); + printf("dev: %lx\n", (unsigned long)statbuf.st_dev); + printf("ino: %lu\n", (unsigned long)statbuf.st_ino); + printf("nlink: %lu\n", (unsigned long)statbuf.st_nlink); + printf("rdev: %lx\n", (unsigned long)statbuf.st_rdev); + printf("atime: %lu\n", (unsigned long)statbuf.st_atime); + printf("mtime: %lu\n", (unsigned long)statbuf.st_mtime); + printf("ctime: %lu\n", (unsigned long)statbuf.st_ctime); + printf("blksize: %lu\n", (unsigned long)statbuf.st_blksize); + } + + free(path); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +stat_constructor(void) { + command_define("stat", stat_main); +} diff --git a/bundles/core/sum.c b/bundles/core/sum.c new file mode 100644 index 0000000..1101347 --- /dev/null +++ b/bundles/core/sum.c @@ -0,0 +1,94 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include + +#include "_common.h" + + +#define BUF_SIZE 8192 + + +/** + * + **/ +static int +sum_main(int argc, char** argv) { + const char *name; + int fd; + int cc; + int ch; + int i; + unsigned long checksum; + char buf[BUF_SIZE]; + int r; + + argc--; + argv++; + r = 0; + + while(argc-- > 0) { + name = *argv++; + + if((fd=open(name, O_RDONLY)) < 0) { + perror(name); + r = 1; + continue; + } + + checksum = 0; + while ((cc = read(fd, buf, sizeof(buf))) > 0) { + for (i = 0; i < cc; i++) { + ch = buf[i]; + + if ((checksum & 0x01) != 0) { + checksum = (checksum >> 1) + 0x8000; + } else { + checksum = (checksum >> 1); + } + + checksum = (checksum + ch) & 0xffff; + } + } + + if(cc < 0) { + perror(name); + r = 1; + close(fd); + continue; + } + + close(fd); + printf("%05lu %s\n", checksum, name); + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +sum_constructor(void) { + command_define("sum", sum_main); +} diff --git a/commands/env.c b/bundles/core/sync.c similarity index 76% rename from commands/env.c rename to bundles/core/sync.c index 1339eee..b88abc7 100644 --- a/commands/env.c +++ b/bundles/core/sync.c @@ -14,17 +14,26 @@ You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, see . */ -#include +#include + +#include "_common.h" /** * **/ -int -main(int argc, char **argv, char** envp) { - for(char** var=envp; *var; var++) { - puts(*var); - } +static int +sync_main(int argc, char** argv) { + sync(); return 0; } + + +/** + * + **/ +__attribute__((constructor)) static void +sync_constructor(void) { + command_define("sync", sync_main); +} diff --git a/bundles/core/sysctl.c b/bundles/core/sysctl.c new file mode 100644 index 0000000..246646b --- /dev/null +++ b/bundles/core/sysctl.c @@ -0,0 +1,58 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include + +#include "_common.h" + + +int +sysctl_main(int argc, char **argv) { + size_t size; + char *buf; + + if(argc <= 1) { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + if((sysctlbyname(argv[1], 0, &size, 0, 0))) { + perror(argv[0]); + } else { + buf = malloc(size); + memset(buf, 0, size); + + if((sysctlbyname(argv[1], buf, &size, 0, 0)) < 0) { + perror(argv[0]); + } + + hexdump(buf, size); + free(buf); + } + + + return 0; +} + + +__attribute__((constructor)) static void +sysctl_constructor(void) { + command_define("sysctl", sysctl_main); +} diff --git a/bundles/core/touch.c b/bundles/core/touch.c new file mode 100644 index 0000000..8132a80 --- /dev/null +++ b/bundles/core/touch.c @@ -0,0 +1,80 @@ +/* Copyright (C) 2021 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +// Code inspired by http://members.tip.net.au/%7Edbell/programs/sash-3.8.tar.gz + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "_common.h" + + +/** + * + **/ +static int +touch_main(int argc, char** argv) { + const char *name; + int fd; + struct utimbuf now; + int r; + + r = 0; + time(&now.actime); + now.modtime = now.actime; + + while(argc-- > 1) { + name = *(++argv); + + if((fd = open(name, O_CREAT | O_WRONLY | O_EXCL, 0666)) >= 0) { + close(fd); + continue; + } + + if(errno != EEXIST) { + perror(name); + r = 1; + continue; + } + + if(errno != EEXIST) { + perror(name); + continue; + } + + if(utime(name, &now) < 0) { + perror(name); + r = 1; + } + } + + return r; +} + + +/** + * + **/ +__attribute__((constructor)) static void +touch_constructor(void) { + command_define("touch", touch_main); +} diff --git a/bundles/core/umount.c b/bundles/core/umount.c new file mode 100644 index 0000000..61bc87b --- /dev/null +++ b/bundles/core/umount.c @@ -0,0 +1,77 @@ +/* Copyright (C) 2024 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include + +#include "_common.h" + + +#define MNT_FORCE 0x0000000000080000ULL + + +static int +umount2(const char *path, uint64_t flags) { + return syscall(22, path, flags); +} + + +static int +umount_main(int argc, char **argv) { + uint64_t flags = 0; + char *path = NULL; + int c; + + while ((c = getopt(argc, argv, "fh")) != -1) { + switch (c) { + case 'f': + flags |= MNT_FORCE; + break; + + case 'h': + default: + printf("usage: %s [-f] \n", argv[0]); + exit(1); + break; + } + } + + if(optind < argc) { + path = abspath(argv[optind]); + } else { + fprintf(stderr, "%s: missing operand\n", argv[0]); + return -1; + } + + if(umount2(path, flags)) { + perror(path); + } + + free(path); + + return 0; +} + + +/** + * + **/ +__attribute__((constructor)) static void +umount_constructor(void) { + command_define("umount", umount_main); +} diff --git a/bundles/http2_get/Makefile b/bundles/http2_get/Makefile new file mode 100644 index 0000000..6d056fe --- /dev/null +++ b/bundles/http2_get/Makefile @@ -0,0 +1,37 @@ +# Copyright (C) 2024 John Törnblom +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not see +# . + +ifdef PS5_PAYLOAD_SDK + include $(PS5_PAYLOAD_SDK)/make/x86_64-ps5-payload.inc +else + $(error PS5_PAYLOAD_SDK is undefined) +endif + +CFLAGS += -Wall +LDADD += -lkernel_sys -lSceNet -lSceSsl -lSceHttp2 + +all: http2_get.elf.inc + +http2_get.elf.inc: http2_get.elf + xxd -i $^ > $@ + +http2_get.elf: main.o + $(LD) $^ $(LDADD) -o $@ + +clean: + rm -f *.o *.elf *.elf.c + +test: diff --git a/bundles/http2_get/main.c b/bundles/http2_get/main.c new file mode 100644 index 0000000..6c272e5 --- /dev/null +++ b/bundles/http2_get/main.c @@ -0,0 +1,225 @@ +/* Copyright (C) 2024 John Törnblom + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, see +. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +int sceNetInit(); +int sceNetPoolCreate(const char*, int, int); +int sceNetPoolDestroy(int); + +int sceSslInit(size_t); +int sceSslTerm(int); + +int sceHttp2Init(int, int, size_t, int); +int sceHttp2Term(int); + +int sceHttp2CreateTemplate(int, const char*, int, int); +int sceHttp2DeleteTemplate(int); + +int sceHttp2CreateRequestWithURL(int, const char*, const char*, uint64_t); +int sceHttp2DeleteRequest(int); + +int sceHttp2SendRequest(int, const void*, size_t); +int sceHttp2GetStatusCode(int, int*); +int sceHttp2ReadData(int, void *, size_t); + + +static int g_libnetMemId = -1; +static int g_libsslCtxId = -1; +static int g_libhttpCtxId = -1; +static int g_tmplId = -1; +static int g_reqId = -1; + + +/** + * + **/ +static int +http2_init(const char* agent, const char* url) { + if(sceNetInit()) { + perror("sceNetInit"); + return -1; + } + + if((g_libnetMemId=sceNetPoolCreate("http2_get", 32*1024, 0)) < 0) { + perror("sceNetPoolCreate"); + return -1; + } + + if((g_libsslCtxId=sceSslInit(256*1024)) < 0) { + perror("sceSslInit"); + return -1; + } + + if((g_libhttpCtxId=sceHttp2Init(g_libnetMemId, g_libsslCtxId, + 256*1024, 1)) < 0) { + perror("sceHttp2Init"); + return -1; + } + + if((g_tmplId=sceHttp2CreateTemplate(g_libhttpCtxId, agent, 3, 1)) < 0) { + perror("sceHttp2CreateTemplate"); + return -1; + } + + if((g_reqId=sceHttp2CreateRequestWithURL(g_tmplId, "GET", url, 0)) < 0) { + perror("sceHttp2CreateRequestWithURL"); + return -1; + } + + return 0; +} + + +/** + * + **/ +static int +http2_get(int outfd) { + char buf[0x1000]; + int length = -1; + int status = -1; + + if(sceHttp2SendRequest(g_reqId, NULL, 0)) { + perror("sceHttp2SendRequest"); + return -1; + } + + if(sceHttp2GetStatusCode(g_reqId, &status)) { + perror("sceHttp2GetStatusCode"); + return -1; + } + + if(status == 200) { + while((length=sceHttp2ReadData(g_reqId, buf, sizeof(buf)-1)) > 0) { + if(write(outfd, buf, length) != length) { + perror("write"); + return -1; + } + } + } else { + printf("Status: %d\n", status); + } + + return status; +} + + +/** + * + **/ +static void +http2_fini(void) { + if(g_reqId != -1) { + if(sceHttp2DeleteRequest(g_reqId)) { + perror("sceHttp2DeleteRequest"); + } + } + + if(g_tmplId != -1) { + if(sceHttp2DeleteTemplate(g_tmplId)) { + perror("sceHttp2DeleteTemplate"); + } + } + + if(g_libhttpCtxId != -1) { + if(sceHttp2Term(g_libhttpCtxId)) { + perror("sceHttp2Term"); + } + } + + if(g_libsslCtxId != -1) { + if(sceSslTerm(g_libsslCtxId)) { + perror("sceSslTerm"); + } + } + + if(g_libnetMemId != -1) { + if(sceNetPoolDestroy(g_libnetMemId)) { + perror("sceNetPoolDestroy"); + } + } +} + + +static void +http2_usage(const char *cmd) { + printf("usage: %s [-o PATH] \n", cmd); +} + + +static char* +abspath(const char *relpath) { + char buf[PATH_MAX]; + if(relpath[0] == '/') { + strncpy(buf, relpath, sizeof(buf)); + } else { + snprintf(buf, sizeof(buf), "%s/%s", getenv("PWD"), relpath); + } + return strdup(buf); +} + +/** + * + **/ +int +main(int argc, char** argv) { + int fdout = STDOUT_FILENO; + char* path = NULL; + int error = 0; + int c; + + if(argc < 2) { + http2_usage(argv[0]); + return 1; + } + + while((c=getopt(argc, argv, "o:")) != -1) { + switch (c) { + case 'o': + path = abspath(optarg); + break; + default: + http2_usage(argv[0]); + return 1; + } + } + + if(path && (fdout=open(path, O_WRONLY | O_TRUNC | O_CREAT, + S_IRUSR | S_IWUSR)) < 0) { + perror(path); + return 1; + } + + if(!(error=http2_init("http2_get/1.0", argv[optind]))) { + error = http2_get(fdout); + } + + http2_fini(); + + return error; +} + diff --git a/commands/.gitignore b/commands/.gitignore deleted file mode 100644 index 14cf862..0000000 --- a/commands/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*_elf.c - diff --git a/elfldr.c b/elfldr.c index b857c92..bdf67aa 100644 --- a/elfldr.c +++ b/elfldr.c @@ -73,10 +73,19 @@ typedef struct elfldr_ctx { static const char* SceSpZeroConf = "/system/vsh/app/NPXS40112/eboot.bin"; +/** + * + **/ int sceKernelSpawn(int *pid, int dbg, const char *path, char *root, char* argv[]); +/** + * + **/ +extern char** environ; + + /** * Parse a R_X86_64_RELATIVE relocatable. **/ @@ -468,10 +477,10 @@ elfldr_prepare_exec(pid_t pid, uint8_t *elf) { } r.r_rip = entry; - r.r_rsi = pt_getint(pid, r.r_rdi); // param 1 - r.r_rdx = r.r_rdi + 0x8; // param 2 - r.r_rcx = elfldr_envp(pid); // param 3 - r.r_rdi = args; // param 0 + r.r_rcx = elfldr_envp(pid); + r.r_rdx = r.r_rsi; // argv + r.r_rsi = r.r_rdi; // argc + r.r_rdi = args; if(pt_setregs(pid, &r)) { perror("pt_setregs"); diff --git a/sh.c b/sh.c index c8fd72c..fcd9a34 100644 --- a/sh.c +++ b/sh.c @@ -25,7 +25,6 @@ along with this program; see the file COPYING. If not, see #include #include "builtin.h" -#include "bundle.h" #include "elfldr.h" @@ -35,6 +34,24 @@ along with this program; see the file COPYING. If not, see #define SHELL_CMD_DELIM "|;&" +typedef struct sce_version { + unsigned long unknown1; + char str_version[0x1c]; + unsigned int bin_version; + unsigned long unknown2; +} sce_version_t; + + +int sceKernelSetProcessName(const char*); +int sceKernelGetSystemSwVersion(sce_version_t *); +int sceKernelGetProsperoSystemSwVersion(sce_version_t *); +int sceKernelGetHwModelName(char *); +int sceKernelGetHwSerialNumber(char *); +long sceKernelGetCpuFrequency(void); +int sceKernelGetCpuTemperature(int *); +int sceKernelGetSocSensorTemperature(int, int *); + + /** * Read a line from stdin. **/ @@ -148,12 +165,96 @@ sh_waitpid(pid_t pid) { } } +/** + * Search the env varriable PATH for a file with the given name. + **/ +static int +sh_which(const char* name, char* path) { + char **paths = NULL; + char* PATH; + + if(name[0] == '/' && !access(name, R_OK | X_OK)) { + strcpy(path, name); + return 0; + } + + PATH = strdup(getenv("PATH")); + if(!(paths=sh_splitstring(PATH, ":"))) { + free(PATH); + return 0; + } + + for(int i=0; paths[i]; i++) { + sprintf(path, "%s/%s", paths[i], name); + if(!access(path, R_OK | X_OK)) { + free(paths); + free(PATH); + return 0; + } + } + + free(paths); + free(PATH); + + return -1; +} + + +/** + * Read a file from disk at the given path. + **/ +static uint8_t* +sh_readfile(const char* path) { + uint8_t* buf; + ssize_t len; + FILE* file; + + if(!(file=fopen(path, "rb"))) { + perror("fopen"); + return 0; + } + + if(fseek(file, 0, SEEK_END)) { + perror("fseek"); + return 0; + } + + if((len=ftell(file)) < 0) { + perror("ftell"); + return 0; + } + + if(fseek(file, 0, SEEK_SET)) { + perror("fseek"); + return 0; + } + + if(!(buf=malloc(len))) { + return 0; + } + + if(fread(buf, 1, len, file) != len) { + perror("fread"); + free(buf); + return 0; + } + + if(fclose(file)) { + perror("fclose"); + free(buf); + return 0; + } + + return buf; +} + /** * Execute a shell command. **/ static int sh_execute(char **argv) { + char path[PATH_MAX]; builtin_cmd_t *cmd; pid_t pid = 0; uint8_t* elf; @@ -171,7 +272,11 @@ sh_execute(char **argv) { return cmd(argc, argv); } - if((elf=bundle_find_elf(argv[0]))) { + if(!sh_which(argv[0], path) && (elf=sh_readfile(path))) { + pid = elfldr_spawn(STDOUT_FILENO, elf, argv); + free(elf); + + } else if((elf=builtin_find_elf(argv[0]))) { pid = elfldr_spawn(STDOUT_FILENO, elf, argv); } @@ -208,7 +313,50 @@ sh_prompt(void) { **/ static void sh_greet(void) { - printf("Hello, world!\n"); + sce_version_t v; + char s[1000]; + int temp = 0; + + printf("\n"); + printf("Welcome to shsrv.elf running on pid %d, ", getppid()); + printf("compiled %s at %s\n\n", __DATE__, __TIME__); + + s[0] = '\0'; + if(sceKernelGetHwModelName(s)) { + perror("sceKernelGetHwModelName"); + } else { + printf("Model: %20s\n", s); + } + + if(sceKernelGetHwSerialNumber(s)) { + perror("sceKernelGetHwSerialNumber"); + } else { + printf("S/N: %20s\n", s); + } + + if(sceKernelGetProsperoSystemSwVersion(&v)) { + perror("sceKernelGetSystemSwVersion"); + } else { + printf("S/W: %20s\n", v.str_version); + } + + if(sceKernelGetSocSensorTemperature(0, &temp)) { + perror("sceKernelGetSocSensorTemperature"); + } else { + printf("SoC temp: %d °C\n", temp); + } + + if(sceKernelGetCpuTemperature(&temp)) { + perror("sceKernelGetCpuTemperature"); + } else { + printf("CPU temp: %d °C\n", temp); + } + + printf("CPU freq: %4ld MHz\n", + sceKernelGetCpuFrequency() / (1000*1000)); + + printf("\nType 'help' for a list of commands\n"); + printf("\n"); } diff --git a/shsrv.c b/shsrv.c index a12eddd..3300693 100644 --- a/shsrv.c +++ b/shsrv.c @@ -32,7 +32,7 @@ along with this program; see the file COPYING. If not, see #include #include "elfldr.h" -#include "sh_elf.c" +#include "sh.elf.inc" /**