1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-28 04:07:46 +00:00

Userland: Move command-line utilities to Userland/Utilities/

This commit is contained in:
Andreas Kling 2021-01-12 11:57:58 +01:00
parent c4e2fd8123
commit ececac65c2
142 changed files with 58 additions and 58 deletions

View file

@ -0,0 +1,49 @@
file(GLOB CMD_SOURCES CONFIGURE_DEPENDS "*.cpp")
list(APPEND SPECIAL_TARGETS "test" "install")
foreach(CMD_SRC ${CMD_SOURCES})
get_filename_component(CMD_NAME ${CMD_SRC} NAME_WE)
if (CMD_NAME IN_LIST SPECIAL_TARGETS)
add_executable("${CMD_NAME}-bin" ${CMD_SRC})
target_link_libraries("${CMD_NAME}-bin" LibCore)
install(TARGETS "${CMD_NAME}-bin" RUNTIME DESTINATION bin)
install(CODE "execute_process(COMMAND mv ${CMD_NAME}-bin ${CMD_NAME} WORKING_DIRECTORY ${CMAKE_INSTALL_PREFIX}/bin)")
else ()
add_executable(${CMD_NAME} ${CMD_SRC})
target_link_libraries(${CMD_NAME} LibCore)
install(TARGETS ${CMD_NAME} RUNTIME DESTINATION bin)
endif()
endforeach()
target_link_libraries(aplay LibAudio)
target_link_libraries(avol LibAudio)
target_link_libraries(checksum LibCrypto)
target_link_libraries(copy LibGUI)
target_link_libraries(disasm LibX86)
target_link_libraries(expr LibRegex)
target_link_libraries(functrace LibDebug LibX86)
target_link_libraries(gml-format LibGUI)
target_link_libraries(html LibWeb)
target_link_libraries(js LibJS LibLine)
target_link_libraries(keymap LibKeyboard)
target_link_libraries(lspci LibPCIDB)
target_link_libraries(man LibMarkdown)
target_link_libraries(md LibMarkdown)
target_link_libraries(misbehaving-application LibCore)
target_link_libraries(notify LibGUI)
target_link_libraries(open LibDesktop)
target_link_libraries(pape LibGUI)
target_link_libraries(passwd LibCrypt)
target_link_libraries(paste LibGUI)
target_link_libraries(pro LibProtocol)
target_link_libraries(su LibCrypt)
target_link_libraries(tar LibTar LibCompress)
target_link_libraries(test-crypto LibCrypto LibTLS LibLine)
target_link_libraries(test-compress LibCompress)
target_link_libraries(test-gfx-font LibGUI LibCore)
target_link_libraries(test-js LibJS LibLine LibCore)
target_link_libraries(test-pthread LibThread)
target_link_libraries(test-web LibWeb)
target_link_libraries(tt LibPthread)
target_link_libraries(grep LibRegex)
target_link_libraries(gunzip LibCompress)

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <math.h>
#include <sys/time.h>
int main(int argc, char** argv)
{
#ifdef __serenity__
if (pledge("stdio settime", nullptr) < 0) {
perror("pledge");
return 1;
}
#endif
Core::ArgsParser args_parser;
double delta = __builtin_nan("");
args_parser.add_option(delta, "Adjust system time by this many seconds", "set", 's', "delta_seconds");
args_parser.parse(argc, argv);
if (__builtin_isnan(delta)) {
#ifdef __serenity__
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
#endif
} else {
long delta_us = static_cast<long>(round(delta * 1'000'000));
timeval delta_timeval;
delta_timeval.tv_sec = delta_us / 1'000'000;
delta_timeval.tv_usec = delta_us % 1'000'000;
if (delta_timeval.tv_usec < 0) {
delta_timeval.tv_sec--;
delta_timeval.tv_usec += 1'000'000;
}
if (adjtime(&delta_timeval, nullptr) < 0) {
perror("adjtime set");
return 1;
}
}
timeval remaining_delta_timeval;
if (adjtime(nullptr, &remaining_delta_timeval) < 0) {
perror("adjtime get");
return 1;
}
double remaining_delta = remaining_delta_timeval.tv_sec + remaining_delta_timeval.tv_usec / 1'000'000.0;
printf("%f\n", remaining_delta);
return 0;
}

View file

@ -0,0 +1,130 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibCore/ElapsedTimer.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static void usage()
{
printf("usage: allocate [number [unit (B/KiB/MiB)]]\n");
exit(1);
}
enum class Unit {
Bytes,
KiB,
MiB,
};
int main(int argc, char** argv)
{
int count = 50;
auto unit = Unit::MiB;
if (argc >= 2) {
auto number = String(argv[1]).to_uint();
if (!number.has_value()) {
usage();
}
count = number.value();
}
if (argc >= 3) {
if (strcmp(argv[2], "B") == 0)
unit = Unit::Bytes;
else if (strcmp(argv[2], "KiB") == 0)
unit = Unit::KiB;
else if (strcmp(argv[2], "MiB") == 0)
unit = Unit::MiB;
else
usage();
}
switch (unit) {
case Unit::Bytes:
break;
case Unit::KiB:
count *= KiB;
break;
case Unit::MiB:
count *= MiB;
break;
}
Core::ElapsedTimer timer;
printf("allocating memory (%d bytes)...\n", count);
timer.start();
char* ptr = (char*)malloc(count);
if (!ptr) {
printf("failed.\n");
return 1;
}
printf("done in %dms\n", timer.elapsed());
auto pages = count / PAGE_SIZE;
auto step = pages / 10;
Core::ElapsedTimer timer2;
printf("writing one byte to each page of allocated memory...\n");
timer.start();
timer2.start();
for (int i = 0; i < pages; ++i) {
ptr[i * PAGE_SIZE] = 1;
if (i != 0 && (i % step) == 0) {
auto ms = timer2.elapsed();
if (ms == 0)
ms = 1;
auto bps = double(step * PAGE_SIZE) / (double(ms) / 1000);
printf("step took %dms (%fMiB/s)\n", ms, bps / MiB);
timer2.start();
}
}
printf("done in %dms\n", timer.elapsed());
printf("sleeping for ten seconds...\n");
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
sleep(1);
}
printf("done.\n");
printf("freeing memory...\n");
timer.start();
free(ptr);
printf("done in %dms\n", timer.elapsed());
return 0;
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibAudio/Buffer.h>
#include <LibAudio/ClientConnection.h>
#include <LibAudio/Loader.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <stdio.h>
int main(int argc, char** argv)
{
const char* path = nullptr;
bool should_loop = false;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path to audio file", "path");
args_parser.add_option(should_loop, "Loop playback", "loop", 'l');
args_parser.parse(argc, argv);
Core::EventLoop loop;
auto audio_client = Audio::ClientConnection::construct();
audio_client->handshake();
NonnullRefPtr<Audio::Loader> loader = Audio::Loader::create(path);
if (loader->has_error()) {
fprintf(stderr, "Failed to load audio file: %s\n", loader->error_string());
return 1;
}
printf("\033[34;1m Playing\033[0m: %s\n", path);
printf("\033[34;1m Format\033[0m: %u Hz, %u-bit, %s\n",
loader->sample_rate(),
loader->bits_per_sample(),
loader->num_channels() == 1 ? "Mono" : "Stereo");
printf("\033[34;1mProgress\033[0m: \033[s");
for (;;) {
auto samples = loader->get_more_samples();
if (samples) {
printf("\033[u");
printf("%d/%d", loader->loaded_samples(), loader->total_samples());
fflush(stdout);
audio_client->enqueue(*samples);
} else if (should_loop) {
loader->reset();
} else if (audio_client->get_remaining_samples()) {
sleep(1);
} else {
break;
}
}
printf("\n");
return 0;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/File.h>
#include <stdio.h>
int main()
{
auto file = Core::File::construct("/proc/net/arp");
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", file->error_string());
return 1;
}
printf("Address HWaddress\n");
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
json.value().as_array().for_each([](auto& value) {
auto if_object = value.as_object();
auto ip_address = if_object.get("ip_address").to_string();
auto mac_address = if_object.get("mac_address").to_string();
printf("%-15s ", ip_address.characters());
printf("%-17s ", mac_address.characters());
printf("\n");
});
return 0;
}

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibAudio/Buffer.h>
#include <LibAudio/ClientConnection.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <stdio.h>
int main(int argc, char** argv)
{
Core::EventLoop loop;
auto audio_client = Audio::ClientConnection::construct();
audio_client->handshake();
bool mute = false;
bool unmute = false;
// FIXME: What is a good way to have an optional int argument?
const char* volume = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(mute, "Mute volume", "mute", 'm');
args_parser.add_option(unmute, "Unmute volume", "unmute", 'M');
args_parser.add_positional_argument(volume, "Volume to set", "volume", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (!mute && !unmute && !volume) {
auto volume = audio_client->get_main_mix_volume();
printf("Volume: %d\n", volume);
return 0;
}
if (!(mute ^ unmute ^ (volume != nullptr))) {
fprintf(stderr, "Only one of mute, unmute or volume must be used\n");
return 1;
}
if (mute) {
audio_client->set_muted(true);
printf("Muted.\n");
} else if (unmute) {
audio_client->set_muted(false);
printf("Unmuted.\n");
} else {
auto new_volume = atoi(volume);
audio_client->set_main_mix_volume(new_volume);
}
return 0;
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020, Tom Lebreux <tomlebreux@hotmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Base64.h>
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool decode = false;
const char* filepath = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(decode, "Decode data", "decode", 'd');
args_parser.add_positional_argument(filepath, "", "file", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
ByteBuffer buffer;
if (filepath == nullptr || strcmp(filepath, "-") == 0) {
auto file = Core::File::construct();
bool success = file->open(
STDIN_FILENO,
Core::IODevice::OpenMode::ReadOnly,
Core::File::ShouldCloseFileDescriptor::Yes);
ASSERT(success);
buffer = file->read_all();
} else {
auto result = Core::File::open(filepath, Core::IODevice::OpenMode::ReadOnly);
ASSERT(!result.is_error());
auto file = result.value();
buffer = file->read_all();
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
if (decode) {
auto decoded = decode_base64(StringView(buffer));
fwrite(decoded.data(), sizeof(u8), decoded.size(), stdout);
return 0;
}
auto encoded = encode_base64(buffer);
printf("%s\n", encoded.characters());
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <LibCore/ArgsParser.h>
#include <stdio.h>
int main(int argc, char** argv)
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path to get basename from", "path");
args_parser.parse(argc, argv);
printf("%s\n", LexicalPath(path).basename().characters());
return 0;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <unistd.h>
int main()
{
sysbeep();
return 0;
}

168
Userland/Utilities/cal.cpp Normal file
View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
const int line_width = 70;
const int line_count = 8;
const int column_width = 22;
char print_buffer[line_width * line_count];
char temp_buffer[line_width * 8];
int target_year;
int target_month;
int target_day;
int current_year;
int current_month;
static void append_to_print(char* buffer, int row, int column, char* text)
{
int starting_point = (line_width * row) + (column * column_width);
for (int i = 0; text[i] != '\0'; i++) {
buffer[starting_point + i] = text[i];
}
}
static void insert_month_to_print(int column, int month, int year)
{
int printing_column = column;
int printing_row = 0;
// FIXME: Both the month name and month header text should be provided by a locale
sprintf(temp_buffer, " %02u - %04u ", month, year);
append_to_print(print_buffer, printing_row, printing_column, temp_buffer);
printing_row++;
sprintf(temp_buffer, "Su Mo Tu We Th Fr Sa");
append_to_print(print_buffer, printing_row, printing_column, temp_buffer);
printing_row++;
int day_to_print = 1;
auto date_time = Core::DateTime::create(year, month, 1);
int first_day_of_week_for_month = date_time.weekday();
int days_in_the_month = date_time.days_in_month();
int last_written_chars = 0;
for (int i = 1; day_to_print <= days_in_the_month; ++i) {
if (i - 1 < first_day_of_week_for_month) {
last_written_chars += sprintf(temp_buffer + last_written_chars, " ");
} else {
if (year == current_year && month == current_month && target_day == day_to_print) {
// FIXME: To replicate Unix cal it would be better to use "\x1b[30;47m%2d\x1b[0m " in here instead of *.
// However, doing that messes up the layout.
last_written_chars += sprintf(temp_buffer + last_written_chars, "%2d*", day_to_print);
} else {
last_written_chars += sprintf(temp_buffer + last_written_chars, "%2d ", day_to_print);
}
day_to_print++;
}
append_to_print(print_buffer, printing_row, printing_column, temp_buffer);
if (i % 7 == 0) {
printing_row++;
memset(temp_buffer, ' ', line_width * 8);
temp_buffer[line_width * 8 - 1] = '\0';
last_written_chars = 0;
}
}
}
static void clean_buffers()
{
for (int i = 1; i < line_width * line_count; ++i) {
print_buffer[i - 1] = i % line_width == 0 ? '\n' : ' ';
}
print_buffer[line_width * line_count - 1] = '\0';
for (int i = 0; i < line_width; ++i) {
temp_buffer[i] = ' ';
}
temp_buffer[line_width - 1] = '\0';
}
int main(int argc, char** argv)
{
int day = 0;
int month = 0;
int year = 0;
Core::ArgsParser args_parser;
args_parser.set_general_help("Display a nice overview of a month or year, defaulting to the current month.");
// FIXME: This should ensure two values get parsed as month + year
args_parser.add_positional_argument(day, "Day of year", "day", Core::ArgsParser::Required::No);
args_parser.add_positional_argument(month, "Month", "month", Core::ArgsParser::Required::No);
args_parser.add_positional_argument(year, "Year", "year", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
time_t now = time(nullptr);
auto* tm = localtime(&now);
// Hack: workaround two values parsing as day + month.
if (day && month && !year) {
year = month;
month = day;
day = 0;
}
bool year_mode = !day && !month && year;
if (!year)
year = tm->tm_year + 1900;
if (!month)
month = tm->tm_mon + 1;
if (!day)
day = tm->tm_mday;
current_year = year;
current_month = month;
clean_buffers();
if (year_mode) {
printf(" ");
printf("Year %4d", year);
printf(" \n\n");
for (int i = 1; i < 12; ++i) {
insert_month_to_print(0, i++, year);
insert_month_to_print(1, i++, year);
insert_month_to_print(2, i, year);
printf("%s\n", print_buffer);
clean_buffers();
}
} else {
insert_month_to_print(0, month, year);
printf("%s\n\n", print_buffer);
clean_buffers();
}
return 0;
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.set_general_help("Concatenate files or pipes to stdout.");
args_parser.add_positional_argument(paths, "File path", "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
Vector<int> fds;
if (!paths.is_empty()) {
for (const char* path : paths) {
int fd;
if (StringView { path } == "-") {
fd = 0;
} else if ((fd = open(path, O_RDONLY)) == -1) {
fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
continue;
}
fds.append(fd);
}
} else {
fds.append(0);
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
for (auto& fd : fds) {
for (;;) {
char buf[32768];
ssize_t nread = read(fd, buf, sizeof(buf));
if (nread == 0)
break;
if (nread < 0) {
perror("read");
return 2;
}
size_t already_written = 0;
while (already_written < (size_t)nread) {
ssize_t nwritten = write(1, buf + already_written, nread - already_written);
if (nwritten < 0) {
perror("write");
return 3;
}
already_written += nwritten;
}
}
close(fd);
}
return 0;
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCrypto/Hash/HashManager.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
auto program_name = StringView { argv[0] };
auto hash_kind { Crypto::Hash::HashKind::None };
if (program_name == "md5sum")
hash_kind = Crypto::Hash::HashKind::MD5;
else if (program_name == "sha1sum")
hash_kind = Crypto::Hash::HashKind::SHA1;
else if (program_name == "sha256sum")
hash_kind = Crypto::Hash::HashKind::SHA256;
else if (program_name == "sha512sum")
hash_kind = Crypto::Hash::HashKind::SHA512;
if (hash_kind == Crypto::Hash::HashKind::None) {
fprintf(stderr, "Error: program must be executed as 'md5sum', 'sha1sum', 'sha256sum' or 'sha512sum'; got '%s'\n", argv[0]);
exit(1);
}
auto hash_name = program_name.substring_view(0, program_name.length() - 3).to_string().to_uppercase();
auto paths_help_string = String::format("File(s) to print %s checksum of", hash_name.characters());
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(paths, paths_help_string.characters(), "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (paths.is_empty())
paths.append("-");
Crypto::Hash::Manager hash;
hash.initialize(hash_kind);
bool success;
auto has_error = false;
auto file = Core::File::construct();
for (auto path : paths) {
if (StringView { path } == "-") {
success = file->open(STDIN_FILENO, Core::IODevice::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::No);
} else {
file->set_filename(path);
success = file->open(Core::IODevice::OpenMode::ReadOnly);
}
if (!success) {
fprintf(stderr, "%s: %s: %s\n", argv[0], path, file->error_string());
has_error = true;
continue;
}
hash.update(file->read_all());
auto digest = hash.digest();
auto digest_data = digest.immutable_data();
StringBuilder builder;
for (size_t i = 0; i < hash.digest_size(); ++i)
builder.appendf("%02x", digest_data[i]);
auto hash_sum_hex = builder.build();
printf("%s %s\n", hash_sum_hex.characters(), path);
}
return has_error ? 1 : 0;
}

View file

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath chown", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* gid_arg = nullptr;
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("Change the owning group for a file or directory.");
args_parser.add_positional_argument(gid_arg, "Group ID", "gid");
args_parser.add_positional_argument(path, "Path to file", "path");
args_parser.parse(argc, argv);
gid_t new_gid = -1;
if (String(gid_arg).is_empty()) {
fprintf(stderr, "Empty gid option\n");
return 1;
}
auto number = String(gid_arg).to_uint();
if (number.has_value()) {
new_gid = number.value();
} else {
auto* group = getgrnam(gid_arg);
if (!group) {
fprintf(stderr, "Unknown group '%s'\n", gid_arg);
return 1;
}
new_gid = group->gr_gid;
}
int rc = chown(path, -1, new_gid);
if (rc < 0) {
perror("chgrp");
return 1;
}
return 0;
}

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/StdLibExtras.h>
#include <AK/kmalloc.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* the new mode will be computed using the boolean function(for each bit):
|current mode|removal mask|applying mask|result |
| 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 |
| 0 | 1 | 0 | 0 |
| 0 | 1 | 1 | 1 | ---> find the CNF --> find the minimal CNF
| 1 | 0 | 0 | 1 |
| 1 | 0 | 1 | 1 |
| 1 | 1 | 0 | 0 |
| 1 | 1 | 1 | 1 |
*/
class Mask {
private:
mode_t removal_mask; // the bits that will be removed
mode_t applying_mask; // the bits that will be set
public:
Mask()
: removal_mask(0)
, applying_mask(0)
{
}
Mask& operator|=(const Mask& other)
{
removal_mask |= other.removal_mask;
applying_mask |= other.applying_mask;
return *this;
}
mode_t& get_removal_mask() { return removal_mask; }
mode_t& get_applying_mask() { return applying_mask; }
};
Optional<Mask> string_to_mode(char access_scope, const char*& access_string);
Optional<Mask> apply_permission(char access_scope, char permission, char operation);
int main(int argc, char** argv)
{
if (pledge("stdio rpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
if (argc < 3) {
printf("usage: chmod <octal-mode> <path...>\n"
" chmod [[ugoa][+-=][rwx...],...] <path...>\n");
return 1;
}
Mask mask;
/* compute a mask */
if (argv[1][0] >= '0' && argv[1][0] <= '7') {
if (sscanf(argv[1], "%ho", &mask.get_applying_mask()) != 1) {
perror("sscanf");
return 1;
}
mask.get_removal_mask() = ~mask.get_applying_mask();
} else {
const char* access_string = argv[1];
while (*access_string != '\0') {
Optional<Mask> tmp_mask;
switch (*access_string) {
case 'u':
tmp_mask = string_to_mode('u', ++access_string);
break;
case 'g':
tmp_mask = string_to_mode('g', ++access_string);
break;
case 'o':
tmp_mask = string_to_mode('o', ++access_string);
break;
case 'a':
tmp_mask = string_to_mode('a', ++access_string);
break;
case '=':
case '+':
case '-':
tmp_mask = string_to_mode('a', access_string);
break;
case ',':
++access_string;
continue;
}
if (!tmp_mask.has_value()) {
fprintf(stderr, "chmod: invalid mode: %s\n", argv[1]);
return 1;
}
mask |= tmp_mask.value();
}
}
/* set the mask for each file's permissions */
struct stat current_access;
int i = 2;
while (i < argc) {
if (stat(argv[i], &current_access) != 0) {
perror("stat");
return 1;
}
/* found the minimal CNF by The QuineMcCluskey algorithm and use it */
mode_t mode = mask.get_applying_mask()
| (current_access.st_mode & ~mask.get_removal_mask());
if (chmod(argv[i++], mode) != 0) {
perror("chmod");
}
}
return 0;
}
Optional<Mask> string_to_mode(char access_scope, const char*& access_string)
{
char operation = *access_string;
if (operation != '+' && operation != '-' && operation != '=') {
return {};
}
Mask mask;
if (operation == '=') {
switch (access_scope) {
case 'u':
mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR);
break;
case 'g':
mask.get_removal_mask() = (S_IRGRP | S_IWGRP | S_IXGRP);
break;
case 'o':
mask.get_removal_mask() = (S_IROTH | S_IWOTH | S_IXOTH);
break;
case 'a':
mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR
| S_IRGRP | S_IWGRP | S_IXGRP
| S_IROTH | S_IWOTH | S_IXOTH);
break;
}
operation = '+';
}
access_string++;
while (*access_string != '\0' && *access_string != ',') {
Optional<Mask> tmp_mask;
tmp_mask = apply_permission(access_scope, *access_string, operation);
if (!tmp_mask.has_value()) {
return {};
}
mask |= tmp_mask.value();
access_string++;
}
return mask;
}
Optional<Mask> apply_permission(char access_scope, char permission, char operation)
{
if (permission != 'r' && permission != 'w' && permission != 'x') {
return {};
}
Mask mask;
mode_t tmp_mask = 0;
switch (access_scope) {
case 'u':
switch (permission) {
case 'r':
tmp_mask = S_IRUSR;
break;
case 'w':
tmp_mask = S_IWUSR;
break;
case 'x':
tmp_mask = S_IXUSR;
break;
}
break;
case 'g':
switch (permission) {
case 'r':
tmp_mask = S_IRGRP;
break;
case 'w':
tmp_mask = S_IWGRP;
break;
case 'x':
tmp_mask = S_IXGRP;
break;
}
break;
case 'o':
switch (permission) {
case 'r':
tmp_mask = S_IROTH;
break;
case 'w':
tmp_mask = S_IWOTH;
break;
case 'x':
tmp_mask = S_IXOTH;
break;
}
break;
case 'a':
mask |= apply_permission('u', permission, operation).value();
mask |= apply_permission('g', permission, operation).value();
mask |= apply_permission('o', permission, operation).value();
break;
}
if (operation == '+') {
mask.get_applying_mask() |= tmp_mask;
} else {
mask.get_removal_mask() |= tmp_mask;
}
return mask;
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <AK/Vector.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath chown", nullptr) < 0) {
perror("pledge");
return 1;
}
if (argc < 3) {
printf("usage: chown <uid[:gid]> <path>\n");
return 0;
}
uid_t new_uid = -1;
gid_t new_gid = -1;
auto parts = String(argv[1]).split(':', true);
if (parts.is_empty()) {
fprintf(stderr, "Empty uid/gid spec\n");
return 1;
}
if (parts[0].is_empty() || (parts.size() == 2 && parts[1].is_empty()) || parts.size() > 2) {
fprintf(stderr, "Invalid uid/gid spec\n");
return 1;
}
auto number = parts[0].to_uint();
if (number.has_value()) {
new_uid = number.value();
} else {
auto* passwd = getpwnam(parts[0].characters());
if (!passwd) {
fprintf(stderr, "Unknown user '%s'\n", parts[0].characters());
return 1;
}
new_uid = passwd->pw_uid;
}
if (parts.size() == 2) {
auto number = parts[1].to_uint();
if (number.has_value()) {
new_gid = number.value();
} else {
auto* group = getgrnam(parts[1].characters());
if (!group) {
fprintf(stderr, "Unknown group '%s'\n", parts[1].characters());
return 1;
}
new_gid = group->gr_gid;
}
}
int rc = chown(argv[2], new_uid, new_gid);
if (rc < 0) {
perror("chown");
return 1;
}
return 0;
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int flags = -1;
uid_t chroot_user = 0;
gid_t chroot_group = 0;
const char* path = nullptr;
const char* program = "/bin/Shell";
const char* userspec = "0:0";
Core::ArgsParser args_parser;
args_parser.set_general_help(
"Run a program in a chroot sandbox. During execution, the program "
"sees the given path as '/', and cannot access files outside of it.");
args_parser.add_positional_argument(path, "New root directory", "path");
args_parser.add_positional_argument(program, "Program to run", "program", Core::ArgsParser::Required::No);
Core::ArgsParser::Option userspec_option {
true,
"The uid:gid to use",
"userspec",
'u',
"userpec",
[&userspec](const char* s) {
Vector<StringView> parts = StringView(s).split_view(':', true);
if (parts.size() != 2)
return false;
userspec = s;
return true;
}
};
args_parser.add_option(move(userspec_option));
Core::ArgsParser::Option mount_options {
true,
"Mount options",
"options",
'o',
"options",
[&flags](const char* s) {
flags = 0;
Vector<StringView> parts = StringView(s).split_view(',');
for (auto& part : parts) {
if (part == "defaults")
continue;
else if (part == "nodev")
flags |= MS_NODEV;
else if (part == "noexec")
flags |= MS_NOEXEC;
else if (part == "nosuid")
flags |= MS_NOSUID;
else if (part == "ro")
flags |= MS_RDONLY;
else if (part == "remount")
flags |= MS_REMOUNT;
else if (part == "bind")
fprintf(stderr, "Ignoring -o bind, as it doesn't make sense for chroot\n");
else
return false;
}
return true;
}
};
args_parser.add_option(move(mount_options));
args_parser.parse(argc, argv);
if (chroot_with_mount_flags(path, flags) < 0) {
perror("chroot");
return 1;
}
if (chdir("/") < 0) {
perror("chdir(/)");
return 1;
}
// Failed parsing will silently fail open (uid=0; gid=0);
// 0:0 is also the default when no --userspec argument is provided.
auto parts = String(userspec).split(':', true);
chroot_user = (uid_t)strtol(parts[0].characters(), nullptr, 10);
chroot_group = (uid_t)strtol(parts[1].characters(), nullptr, 10);
if (setresgid(chroot_group, chroot_group, chroot_group)) {
perror("setgid");
return 1;
}
if (setresuid(chroot_user, chroot_user, chroot_user)) {
perror("setuid");
return 1;
}
execl(program, program, nullptr);
perror("execl");
return 1;
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
int main(int, char**)
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
printf("\033[3J\033[H\033[2J");
fflush(stdout);
return 0;
}

View file

@ -0,0 +1,87 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibGUI/Application.h>
#include <LibGUI/Clipboard.h>
#include <stdio.h>
#include <stdlib.h>
struct Options {
String data;
StringView type;
};
static Options parse_options(int argc, char* argv[])
{
const char* type = "text/plain";
Vector<const char*> text;
Core::ArgsParser args_parser;
args_parser.set_general_help("Copy text from stdin or the command-line to the clipboard.");
args_parser.add_option(type, "Pick a type", "type", 't', "type");
args_parser.add_positional_argument(text, "Text to copy", "text", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
Options options;
options.type = type;
if (text.is_empty()) {
// Copy our stdin.
auto c_stdin = Core::File::construct();
bool success = c_stdin->open(
STDIN_FILENO,
Core::IODevice::OpenMode::ReadOnly,
Core::File::ShouldCloseFileDescriptor::No);
ASSERT(success);
auto buffer = c_stdin->read_all();
dbgln("Read size {}", buffer.size());
options.data = String((char*)buffer.data(), buffer.size());
} else {
// Copy the rest of our command-line args.
StringBuilder builder;
builder.join(' ', text);
options.data = builder.to_string();
}
return options;
}
int main(int argc, char* argv[])
{
auto app = GUI::Application::construct(argc, argv);
Options options = parse_options(argc, argv);
auto& clipboard = GUI::Clipboard::the();
clipboard.set_data(options.data.bytes(), options.type);
return 0;
}

221
Userland/Utilities/cp.cpp Normal file
View file

@ -0,0 +1,221 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
bool copy_file_or_directory(String, String, bool, bool);
bool copy_file(String, String, const struct stat&, int);
bool copy_directory(String, String, bool);
static mode_t my_umask = 0;
int main(int argc, char** argv)
{
if (pledge("stdio rpath wpath cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
bool link = false;
bool recursion_allowed = false;
bool verbose = false;
Vector<const char*> sources;
const char* destination = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(link, "Link files instead of copying", "link", 'l');
args_parser.add_option(recursion_allowed, "Copy directories recursively", "recursive", 'R');
args_parser.add_option(recursion_allowed, "Same as -R", nullptr, 'r');
args_parser.add_option(verbose, "Verbose", "verbose", 'v');
args_parser.add_positional_argument(sources, "Source file path", "source");
args_parser.add_positional_argument(destination, "Destination file path", "destination");
args_parser.parse(argc, argv);
auto my_umask = umask(0);
umask(my_umask);
for (auto& source : sources) {
bool ok = copy_file_or_directory(source, destination, recursion_allowed, link);
if (!ok)
return 1;
if (verbose)
printf("'%s' -> '%s'\n", source, destination);
}
return 0;
}
/**
* Copy a file or directory to a new location. Returns true if successful, false
* otherwise. If there is an error, its description is output to stderr.
*
* Directories should only be copied if recursion_allowed is set.
*/
bool copy_file_or_directory(String src_path, String dst_path, bool recursion_allowed, bool link)
{
int src_fd = open(src_path.characters(), O_RDONLY);
if (src_fd < 0) {
perror("open src");
return false;
}
struct stat src_stat;
int rc = fstat(src_fd, &src_stat);
if (rc < 0) {
perror("stat src");
return false;
}
if (S_ISDIR(src_stat.st_mode)) {
if (!recursion_allowed) {
fprintf(stderr, "cp: -R not specified; omitting directory '%s'\n", src_path.characters());
return false;
}
return copy_directory(src_path, dst_path, link);
}
if (link) {
if (::link(src_path.characters(), dst_path.characters()) < 0) {
perror("link");
return false;
}
return true;
}
return copy_file(src_path, dst_path, src_stat, src_fd);
}
/**
* Copy a source file to a destination file. Returns true if successful, false
* otherwise. If there is an error, its description is output to stderr.
*
* To avoid repeated work, the source file's stat and file descriptor are required.
*/
bool copy_file(String src_path, String dst_path, const struct stat& src_stat, int src_fd)
{
// NOTE: We don't copy the set-uid and set-gid bits.
mode_t mode = (src_stat.st_mode & ~my_umask) & ~06000;
int dst_fd = creat(dst_path.characters(), mode);
if (dst_fd < 0) {
if (errno != EISDIR) {
perror("open dst");
return false;
}
StringBuilder builder;
builder.append(dst_path);
builder.append('/');
builder.append(LexicalPath(src_path).basename());
dst_path = builder.to_string();
dst_fd = creat(dst_path.characters(), 0666);
if (dst_fd < 0) {
perror("open dst");
return false;
}
}
if (src_stat.st_size > 0) {
if (ftruncate(dst_fd, src_stat.st_size) < 0) {
perror("cp: ftruncate");
return false;
}
}
for (;;) {
char buffer[32768];
ssize_t nread = read(src_fd, buffer, sizeof(buffer));
if (nread < 0) {
perror("read src");
return false;
}
if (nread == 0)
break;
ssize_t remaining_to_write = nread;
char* bufptr = buffer;
while (remaining_to_write) {
ssize_t nwritten = write(dst_fd, bufptr, remaining_to_write);
if (nwritten < 0) {
perror("write dst");
return false;
}
assert(nwritten > 0);
remaining_to_write -= nwritten;
bufptr += nwritten;
}
}
close(src_fd);
close(dst_fd);
return true;
}
/**
* Copy the contents of a source directory into a destination directory.
*/
bool copy_directory(String src_path, String dst_path, bool link)
{
int rc = mkdir(dst_path.characters(), 0755);
if (rc < 0) {
perror("cp: mkdir");
return false;
}
String src_rp = Core::File::real_path_for(src_path);
src_rp = String::format("%s/", src_rp.characters());
String dst_rp = Core::File::real_path_for(dst_path);
dst_rp = String::format("%s/", dst_rp.characters());
if (!dst_rp.is_empty() && dst_rp.starts_with(src_rp)) {
fprintf(stderr, "cp: Cannot copy %s into itself (%s)\n",
src_path.characters(), dst_path.characters());
return false;
}
Core::DirIterator di(src_path, Core::DirIterator::SkipDots);
if (di.has_error()) {
fprintf(stderr, "cp: DirIterator: %s\n", di.error_string());
return false;
}
while (di.has_next()) {
String filename = di.next_path();
bool is_copied = copy_file_or_directory(
String::format("%s/%s", src_path.characters(), filename.characters()),
String::format("%s/%s", dst_path.characters(), filename.characters()),
true, link);
if (!is_copied) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,329 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2019-2020, Shannon Booth <shannon.ml.booth@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Function.h>
#include <AK/String.h>
#include <Kernel/API/Syscall.h>
#include <Kernel/IO.h>
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#pragma GCC optimize("O0")
class Crash {
public:
enum class RunType {
UsingChildProcess,
UsingCurrentProcess,
};
enum class Failure {
DidNotCrash,
UnexpectedError,
};
Crash(String test_type, Function<Crash::Failure()> crash_function)
: m_type(test_type)
, m_crash_function(move(crash_function))
{
}
void run(RunType run_type)
{
printf("\x1B[33mTesting\x1B[0m: \"%s\"\n", m_type.characters());
auto run_crash_and_print_if_error = [this]() {
auto failure = m_crash_function();
// If we got here something went wrong
printf("\x1B[31mFAIL\x1B[0m: ");
switch (failure) {
case Failure::DidNotCrash:
printf("Did not crash!\n");
break;
case Failure::UnexpectedError:
printf("Unexpected error!\n");
break;
default:
ASSERT_NOT_REACHED();
}
};
if (run_type == RunType::UsingCurrentProcess) {
run_crash_and_print_if_error();
} else {
// Run the test in a child process so that we do not crash the crash program :^)
pid_t pid = fork();
if (pid < 0) {
perror("fork");
ASSERT_NOT_REACHED();
} else if (pid == 0) {
run_crash_and_print_if_error();
exit(0);
}
int status;
waitpid(pid, &status, 0);
if (WIFSIGNALED(status))
printf("\x1B[32mPASS\x1B[0m: Terminated with signal %d\n", WTERMSIG(status));
}
}
private:
String m_type;
Function<Crash::Failure()> m_crash_function;
};
int main(int argc, char** argv)
{
bool do_all_crash_types = false;
bool do_segmentation_violation = false;
bool do_division_by_zero = false;
bool do_illegal_instruction = false;
bool do_abort = false;
bool do_write_to_uninitialized_malloc_memory = false;
bool do_write_to_freed_memory = false;
bool do_write_to_read_only_memory = false;
bool do_read_from_uninitialized_malloc_memory = false;
bool do_read_from_freed_memory = false;
bool do_invalid_stack_pointer_on_syscall = false;
bool do_invalid_stack_pointer_on_page_fault = false;
bool do_syscall_from_writeable_memory = false;
bool do_execute_non_executable_memory = false;
bool do_trigger_user_mode_instruction_prevention = false;
bool do_use_io_instruction = false;
bool do_read_cpu_counter = false;
auto args_parser = Core::ArgsParser();
args_parser.set_general_help(
"Exercise error-handling paths of the execution environment "
"(i.e., Kernel or UE) by crashing in many different ways.");
args_parser.add_option(do_all_crash_types, "Test that all of the following crash types crash as expected", nullptr, 'A');
args_parser.add_option(do_segmentation_violation, "Perform a segmentation violation by dereferencing an invalid pointer", nullptr, 's');
args_parser.add_option(do_division_by_zero, "Perform a division by zero", nullptr, 'd');
args_parser.add_option(do_illegal_instruction, "Execute an illegal CPU instruction", nullptr, 'i');
args_parser.add_option(do_abort, "Call `abort()`", nullptr, 'a');
args_parser.add_option(do_read_from_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then read from it", nullptr, 'm');
args_parser.add_option(do_read_from_freed_memory, "Read a pointer from memory freed using `free()`, then read from it", nullptr, 'f');
args_parser.add_option(do_write_to_uninitialized_malloc_memory, "Read a pointer from uninitialized malloc memory, then write to it", nullptr, 'M');
args_parser.add_option(do_write_to_freed_memory, "Read a pointer from memory freed using `free()`, then write to it", nullptr, 'F');
args_parser.add_option(do_write_to_read_only_memory, "Write to read-only memory", nullptr, 'r');
args_parser.add_option(do_invalid_stack_pointer_on_syscall, "Make a syscall while using an invalid stack pointer", nullptr, 'T');
args_parser.add_option(do_invalid_stack_pointer_on_page_fault, "Trigger a page fault while using an invalid stack pointer", nullptr, 't');
args_parser.add_option(do_syscall_from_writeable_memory, "Make a syscall from writeable memory", nullptr, 'S');
args_parser.add_option(do_execute_non_executable_memory, "Attempt to execute non-executable memory (not mapped with PROT_EXEC)", nullptr, 'X');
args_parser.add_option(do_trigger_user_mode_instruction_prevention, "Attempt to trigger an x86 User Mode Instruction Prevention fault", nullptr, 'U');
args_parser.add_option(do_use_io_instruction, "Use an x86 I/O instruction in userspace", nullptr, 'I');
args_parser.add_option(do_read_cpu_counter, "Read the x86 TSC (Time Stamp Counter) directly", nullptr, 'c');
if (argc != 2) {
args_parser.print_usage(stderr, argv[0]);
exit(1);
}
args_parser.parse(argc, argv);
Crash::RunType run_type = do_all_crash_types ? Crash::RunType::UsingChildProcess
: Crash::RunType::UsingCurrentProcess;
if (do_segmentation_violation || do_all_crash_types) {
Crash("Segmentation violation", []() {
volatile int* crashme = nullptr;
*crashme = 0xbeef;
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_division_by_zero || do_all_crash_types) {
Crash("Division by zero", []() {
volatile int lala = 10;
volatile int zero = 0;
[[maybe_unused]] volatile int test = lala / zero;
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_illegal_instruction || do_all_crash_types) {
Crash("Illegal instruction", []() {
asm volatile("ud2");
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_abort || do_all_crash_types) {
Crash("Abort", []() {
abort();
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
Crash("Read from uninitialized malloc memory", []() {
auto* uninitialized_memory = (volatile u32**)malloc(1024);
if (!uninitialized_memory)
return Crash::Failure::UnexpectedError;
[[maybe_unused]] volatile auto x = uninitialized_memory[0][0];
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_read_from_uninitialized_malloc_memory || do_all_crash_types) {
Crash("Read from freed memory", []() {
auto* uninitialized_memory = (volatile u32**)malloc(1024);
if (!uninitialized_memory)
return Crash::Failure::UnexpectedError;
free(uninitialized_memory);
[[maybe_unused]] volatile auto x = uninitialized_memory[4][0];
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_write_to_uninitialized_malloc_memory || do_all_crash_types) {
Crash("Write to uninitialized malloc memory", []() {
auto* uninitialized_memory = (volatile u32**)malloc(1024);
if (!uninitialized_memory)
return Crash::Failure::UnexpectedError;
uninitialized_memory[4][0] = 1;
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_write_to_freed_memory || do_all_crash_types) {
Crash("Write to freed memory", []() {
auto* uninitialized_memory = (volatile u32**)malloc(1024);
if (!uninitialized_memory)
return Crash::Failure::UnexpectedError;
free(uninitialized_memory);
uninitialized_memory[4][0] = 1;
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_write_to_read_only_memory || do_all_crash_types) {
Crash("Write to read only memory", []() {
auto* ptr = (u8*)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
if (ptr != MAP_FAILED)
return Crash::Failure::UnexpectedError;
*ptr = 'x'; // This should work fine.
int rc = mprotect(ptr, 4096, PROT_READ);
if (rc != 0 || *ptr != 'x')
return Crash::Failure::UnexpectedError;
*ptr = 'y'; // This should crash!
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_invalid_stack_pointer_on_syscall || do_all_crash_types) {
Crash("Invalid stack pointer on syscall", []() {
u8* makeshift_stack = (u8*)mmap(nullptr, 0, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_STACK, 0, 0);
if (!makeshift_stack)
return Crash::Failure::UnexpectedError;
u8* makeshift_esp = makeshift_stack + 2048;
asm volatile("mov %%eax, %%esp" ::"a"(makeshift_esp));
getuid();
dbgln("Survived syscall with MAP_STACK stack");
u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (!bad_stack)
return Crash::Failure::UnexpectedError;
u8* bad_esp = bad_stack + 2048;
asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
getuid();
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_invalid_stack_pointer_on_page_fault || do_all_crash_types) {
Crash("Invalid stack pointer on page fault", []() {
u8* bad_stack = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (!bad_stack)
return Crash::Failure::UnexpectedError;
u8* bad_esp = bad_stack + 2048;
asm volatile("mov %%eax, %%esp" ::"a"(bad_esp));
asm volatile("pushl $0");
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_syscall_from_writeable_memory || do_all_crash_types) {
Crash("Syscall from writable memory", []() {
u8 buffer[] = { 0xb8, Syscall::SC_getuid, 0, 0, 0, 0xcd, 0x82 };
((void (*)())buffer)();
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_execute_non_executable_memory || do_all_crash_types) {
Crash("Execute non executable memory", []() {
auto* ptr = (u8*)mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (ptr == MAP_FAILED)
return Crash::Failure::UnexpectedError;
ptr[0] = 0xc3; // ret
typedef void* (*CrashyFunctionPtr)();
((CrashyFunctionPtr)ptr)();
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_trigger_user_mode_instruction_prevention || do_all_crash_types) {
Crash("Trigger x86 User Mode Instruction Prevention", []() {
asm volatile("str %eax");
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_use_io_instruction || do_all_crash_types) {
Crash("Attempt to use an I/O instruction", [] {
u8 keyboard_status = IO::in8(0x64);
printf("Keyboard status: %#02x\n", keyboard_status);
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
if (do_read_cpu_counter || do_all_crash_types) {
Crash("Read the CPU timestamp counter", [] {
asm volatile("rdtsc");
return Crash::Failure::DidNotCrash;
}).run(run_type);
}
return 0;
}

248
Userland/Utilities/cut.cpp Normal file
View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2019-2020, Marios Prokopakis <mariosprokopakis@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/QuickSort.h>
#include <AK/StdLibExtras.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Index {
enum class Type {
SingleIndex,
SliceIndex,
RangedIndex
};
ssize_t m_from { -1 };
ssize_t m_to { -1 };
Type m_type { Type::SingleIndex };
bool intersects(const Index& other)
{
if (m_type != Type::RangedIndex)
return m_from == other.m_from;
return !(other.m_from > m_to || other.m_to < m_from);
}
};
static void print_usage_and_exit(int ret)
{
printf("Usage: cut -b list [File]\n");
exit(ret);
}
static void add_if_not_exists(Vector<Index>& indexes, Index data)
{
bool append_to_vector = true;
for (auto& index : indexes) {
if (index.intersects(data)) {
if (index.m_type == Index::Type::RangedIndex) {
index.m_from = AK::min(index.m_from, data.m_from);
index.m_to = AK::max(index.m_to, data.m_to);
}
append_to_vector = false;
}
}
if (append_to_vector) {
indexes.append(data);
}
}
static void expand_list(Vector<String>& tokens, Vector<Index>& indexes)
{
for (auto& token : tokens) {
if (token.length() == 0) {
fprintf(stderr, "cut: byte/character positions are numbered from 1\n");
print_usage_and_exit(1);
}
if (token == "-") {
fprintf(stderr, "cut: invalid range with no endpoint: %s\n", token.characters());
print_usage_and_exit(1);
}
if (token[0] == '-') {
auto index = token.substring(1, token.length() - 1).to_int();
if (!index.has_value()) {
fprintf(stderr, "cut: invalid byte/character position '%s'\n", token.characters());
print_usage_and_exit(1);
}
if (index.value() == 0) {
fprintf(stderr, "cut: byte/character positions are numbered from 1\n");
print_usage_and_exit(1);
}
Index tmp = { 1, index.value(), Index::Type::RangedIndex };
add_if_not_exists(indexes, tmp);
} else if (token[token.length() - 1] == '-') {
auto index = token.substring(0, token.length() - 1).to_int();
if (!index.has_value()) {
fprintf(stderr, "cut: invalid byte/character position '%s'\n", token.characters());
print_usage_and_exit(1);
}
if (index.value() == 0) {
fprintf(stderr, "cut: byte/character positions are numbered from 1\n");
print_usage_and_exit(1);
}
Index tmp = { index.value(), -1, Index::Type::SliceIndex };
add_if_not_exists(indexes, tmp);
} else {
auto range = token.split('-');
if (range.size() == 2) {
auto index1 = range[0].to_int();
if (!index1.has_value()) {
fprintf(stderr, "cut: invalid byte/character position '%s'\n", range[0].characters());
print_usage_and_exit(1);
}
auto index2 = range[1].to_int();
if (!index2.has_value()) {
fprintf(stderr, "cut: invalid byte/character position '%s'\n", range[1].characters());
print_usage_and_exit(1);
}
if (index1.value() > index2.value()) {
fprintf(stderr, "cut: invalid decreasing range\n");
print_usage_and_exit(1);
} else if (index1.value() == 0 || index2.value() == 0) {
fprintf(stderr, "cut: byte/character positions are numbered from 1\n");
print_usage_and_exit(1);
}
Index tmp = { index1.value(), index2.value(), Index::Type::RangedIndex };
add_if_not_exists(indexes, tmp);
} else if (range.size() == 1) {
auto index = range[0].to_int();
if (!index.has_value()) {
fprintf(stderr, "cut: invalid byte/character position '%s'\n", range[0].characters());
print_usage_and_exit(1);
}
if (index.value() == 0) {
fprintf(stderr, "cut: byte/character positions are numbered from 1\n");
print_usage_and_exit(1);
}
Index tmp = { index.value(), index.value(), Index::Type::SingleIndex };
add_if_not_exists(indexes, tmp);
} else {
fprintf(stderr, "cut: invalid byte or character range\n");
print_usage_and_exit(1);
}
}
}
}
static void cut_file(const String& file, const Vector<Index>& byte_vector)
{
FILE* fp = stdin;
if (!file.is_null()) {
fp = fopen(file.characters(), "r");
if (!fp) {
fprintf(stderr, "cut: Could not open file '%s'\n", file.characters());
return;
}
}
char* line = nullptr;
ssize_t line_length = 0;
size_t line_capacity = 0;
while ((line_length = getline(&line, &line_capacity, fp)) != -1) {
line[line_length - 1] = '\0';
line_length--;
for (auto& i : byte_vector) {
if (i.m_type == Index::Type::SliceIndex && i.m_from < line_length)
printf("%s", line + i.m_from - 1);
else if (i.m_type == Index::Type::SingleIndex && i.m_from <= line_length)
printf("%c", line[i.m_from - 1]);
else if (i.m_type == Index::Type::RangedIndex && i.m_from <= line_length) {
auto to = i.m_to > line_length ? line_length : i.m_to;
auto sub_string = String(line).substring(i.m_from - 1, to - i.m_from + 1);
printf("%s", sub_string.characters());
} else
break;
}
printf("\n");
}
if (line)
free(line);
if (!file.is_null())
fclose(fp);
}
int main(int argc, char** argv)
{
String byte_list = "";
Vector<String> tokens;
Vector<String> files;
if (argc == 1) {
print_usage_and_exit(1);
}
for (int i = 1; i < argc;) {
if (!strcmp(argv[i], "-b")) {
/* The next argument should be a list of bytes. */
byte_list = (i + 1 < argc) ? argv[i + 1] : "";
if (byte_list == "") {
print_usage_and_exit(1);
}
tokens = byte_list.split(',');
i += 2;
} else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
print_usage_and_exit(1);
} else if (argv[i][0] != '-') {
files.append(argv[i++]);
} else {
fprintf(stderr, "cut: invalid argument %s\n", argv[i]);
print_usage_and_exit(1);
}
}
if (byte_list == "")
print_usage_and_exit(1);
Vector<Index> byte_vector;
expand_list(tokens, byte_vector);
quick_sort(byte_vector, [](auto& a, auto& b) { return a.m_from < b.m_from; });
if (files.is_empty())
files.append(String());
/* Process each file */
for (auto& file : files)
cut_file(file, byte_vector);
return 0;
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
int main(int argc, char** argv)
{
if (pledge("stdio settime", nullptr) < 0) {
perror("pledge");
return 1;
}
bool print_unix_date = false;
bool print_iso_8601 = false;
bool print_rfc_3339 = false;
bool print_rfc_5322 = false;
const char* set_date = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(set_date, "Set system date and time", "set", 's', "date");
args_parser.add_option(print_unix_date, "Print date as Unix timestamp", "unix", 'u');
args_parser.add_option(print_iso_8601, "Print date in ISO 8601 format", "iso-8601", 'i');
args_parser.add_option(print_rfc_3339, "Print date in RFC 3339 format", "rfc-3339", 'r');
args_parser.add_option(print_rfc_5322, "Print date in RFC 5322 format", "rfc-5322", 'R');
args_parser.parse(argc, argv);
if (set_date != nullptr) {
auto number = String(set_date).to_uint();
if (!number.has_value()) {
fprintf(stderr, "date: Invalid timestamp value");
return 1;
}
timespec ts = { number.value(), 0 };
if (clock_settime(CLOCK_REALTIME, &ts) < 0) {
perror("clock_settime");
return 1;
}
return 0;
}
// FIXME: this should be improved and will need to be cleaned up
// when additional output formats and formatting is supported
if (print_unix_date && print_iso_8601 && print_rfc_3339 && print_rfc_5322) {
fprintf(stderr, "date: multiple output formats specified\n");
return 1;
}
time_t now = time(nullptr);
auto date = Core::DateTime::from_timestamp(now);
if (print_unix_date) {
printf("%lld\n", (long long)now);
return 0;
} else if (print_iso_8601) {
printf("%s\n", date.to_string("%Y-%m-%dT%H:%M:%S-00:00").characters());
return 0;
} else if (print_rfc_5322) {
printf("%s\n", date.to_string("%a, %d %b %Y %H:%M:%S -0000").characters());
return 0;
} else if (print_rfc_3339) {
printf("%s\n", date.to_string("%Y-%m-%d %H:%M:%S-00:00").characters());
return 0;
} else {
printf("%s\n", date.to_string().characters());
return 0;
}
}

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2021, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <LibCore/DateTime.h>
#include <stdio.h>
class DiscordianDate {
public:
explicit DiscordianDate(Core::DateTime date)
{
m_gregorian_date = date;
m_yold = date.year() + 1166;
uint16_t day = day_of_yold() + 1;
if (is_leap_year() && day > m_st_tibs_day_of_yold)
--day;
m_day_of_week = day_of_week_from_day_of_yold(day);
m_season = season_from_day_of_yold(day);
m_date = date_from_day_of_yold(day);
}
const char* day_of_week() { return m_day_of_week.characters(); };
const char* season() { return m_season.characters(); };
uint16_t year() { return yold(); };
uint16_t yold() { return m_yold; };
uint16_t day_of_year() { return day_of_yold(); };
uint16_t day_of_yold() { return m_gregorian_date.day_of_year(); };
bool is_leap_year() { return m_gregorian_date.is_leap_year(); };
bool is_st_tibs_day() { return (is_leap_year() && (day_of_yold() + 1) == m_st_tibs_day_of_yold); };
String to_string()
{
if (is_st_tibs_day())
return String::formatted("St. Tib's Day, in the YOLD {}", m_yold);
return String::formatted("{}, day {} of {}, in the YOLD {}", m_day_of_week, m_date, m_season, m_yold);
}
private:
static const int m_days_in_week = 5;
static const int m_days_in_season = 73;
static const int m_st_tibs_day_of_yold = 60;
Core::DateTime m_gregorian_date;
String m_day_of_week;
String m_season;
int m_date;
uint64_t m_yold; // 64-bit for use after X-Day in the YOLD 8661
int date_from_day_of_yold(uint16_t day)
{
return (day % m_days_in_season == 0 ? m_days_in_season : day % m_days_in_season);
}
const char* day_of_week_from_day_of_yold(uint16_t day)
{
if (is_st_tibs_day())
return nullptr;
switch ((day % m_days_in_week) == 0 ? m_days_in_week : (day % m_days_in_week)) {
case 1:
return "Sweetmorn";
case 2:
return "Boomtime";
case 3:
return "Pungenday";
case 4:
return "Prickle-Prickle";
case 5:
return "Setting Orange";
default:
return nullptr;
}
}
const char* season_from_day_of_yold(uint16_t day)
{
if (is_st_tibs_day())
return nullptr;
switch (((day % m_days_in_season) == 0 ? day - 1 : day) / m_days_in_season) {
case 0:
return "Chaos";
case 1:
return "Discord";
case 2:
return "Confusion";
case 3:
return "Bureaucracy";
case 4:
return "The Aftermath";
default:
return nullptr;
}
}
};
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
auto date = Core::DateTime::from_timestamp(time(nullptr));
printf("Today is %s\n", DiscordianDate(date).to_string().characters());
return 0;
}

103
Userland/Utilities/df.cpp Normal file
View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/NumberFormat.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static bool flag_human_readable = false;
struct FileSystem {
String fs;
size_t total_block_count { 0 };
size_t free_block_count { 0 };
size_t total_inode_count { 0 };
size_t free_inode_count { 0 };
size_t block_size { 0 };
String mount_point;
};
int main(int argc, char** argv)
{
Core::ArgsParser args_parser;
args_parser.set_general_help("Display free disk space of each partition.");
args_parser.add_option(flag_human_readable, "Print human-readable sizes", "human-readable", 'h');
args_parser.parse(argc, argv);
auto file = Core::File::construct("/proc/df");
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Failed to open /proc/df: %s\n", file->error_string());
return 1;
}
if (flag_human_readable) {
printf("Filesystem Size Used Available Mount point\n");
} else {
printf("Filesystem Blocks Used Available Mount point\n");
}
auto file_contents = file->read_all();
auto json_result = JsonValue::from_string(file_contents);
ASSERT(json_result.has_value());
auto json = json_result.value().as_array();
json.for_each([](auto& value) {
auto fs_object = value.as_object();
auto fs = fs_object.get("class_name").to_string();
auto total_block_count = fs_object.get("total_block_count").to_u32();
auto free_block_count = fs_object.get("free_block_count").to_u32();
[[maybe_unused]] auto total_inode_count = fs_object.get("total_inode_count").to_u32();
[[maybe_unused]] auto free_inode_count = fs_object.get("free_inode_count").to_u32();
auto block_size = fs_object.get("block_size").to_u32();
auto mount_point = fs_object.get("mount_point").to_string();
printf("%-10s", fs.characters());
if (flag_human_readable) {
printf("%10s ", human_readable_size(total_block_count * block_size).characters());
printf("%10s ", human_readable_size((total_block_count - free_block_count) * block_size).characters());
printf("%10s ", human_readable_size(free_block_count * block_size).characters());
} else {
printf("%10u ", total_block_count);
printf("%10u ", total_block_count - free_block_count);
printf("%10u ", free_block_count);
}
printf("%s", mount_point.characters());
printf("\n");
});
return 0;
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <LibCore/ArgsParser.h>
int main(int argc, char** argv)
{
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path", "path");
args_parser.parse(argc, argv);
outln("{}", LexicalPath(path).dirname());
return 0;
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LogStream.h>
#include <AK/MappedFile.h>
#include <AK/QuickSort.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibELF/Image.h>
#include <LibX86/Disassembler.h>
#include <LibX86/ELFSymbolProvider.h>
#include <stdio.h>
#include <string.h>
//#define DISASM_DUMP
int main(int argc, char** argv)
{
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help(
"Disassemble an executable, and show human-readable "
"assembly code for each function.");
args_parser.add_positional_argument(path, "Path to i386 binary file", "path");
args_parser.parse(argc, argv);
auto file_or_error = MappedFile::map(path);
if (file_or_error.is_error()) {
warnln("Could not map file: {}", file_or_error.error().string());
return 1;
}
auto& file = *file_or_error.value();
struct Symbol {
size_t value;
size_t size;
StringView name;
size_t address() const { return value; }
size_t address_end() const { return value + size; }
bool contains(size_t virtual_address) { return address() <= virtual_address && virtual_address < address_end(); }
};
Vector<Symbol> symbols;
const u8* asm_data = (const u8*)file.data();
size_t asm_size = file.size();
size_t file_offset = 0;
Vector<Symbol>::Iterator current_symbol = symbols.begin();
OwnPtr<X86::ELFSymbolProvider> symbol_provider; // nullptr for non-ELF disassembly.
OwnPtr<ELF::Image> elf;
if (asm_size >= 4 && strncmp((const char*)asm_data, "\u007fELF", 4) == 0) {
elf = make<ELF::Image>(asm_data, asm_size);
if (elf->is_valid()) {
symbol_provider = make<X86::ELFSymbolProvider>(*elf);
elf->for_each_section_of_type(SHT_PROGBITS, [&](const ELF::Image::Section& section) {
// FIXME: Disassemble all SHT_PROGBITS sections, not just .text.
if (section.name() != ".text")
return IterationDecision::Continue;
asm_data = (const u8*)section.raw_data();
asm_size = section.size();
file_offset = section.address();
return IterationDecision::Break;
});
symbols.ensure_capacity(elf->symbol_count() + 1);
symbols.append({ 0, 0, StringView() }); // Sentinel.
elf->for_each_symbol([&](const ELF::Image::Symbol& symbol) {
symbols.append({ symbol.value(), symbol.size(), symbol.name() });
return IterationDecision::Continue;
});
quick_sort(symbols, [](auto& a, auto& b) {
if (a.value != b.value)
return a.value < b.value;
if (a.size != b.size)
return a.size < b.size;
return a.name < b.name;
});
#ifdef DISASM_DUMP
for (size_t i = 0; i < symbols.size(); ++i)
dbg() << symbols[i].name << ": " << (void*)(uintptr_t)symbols[i].value << ", " << symbols[i].size;
#endif
}
}
X86::SimpleInstructionStream stream(asm_data, asm_size);
X86::Disassembler disassembler(stream);
bool is_first_symbol = true;
bool current_instruction_is_in_symbol = false;
for (;;) {
auto offset = stream.offset();
auto insn = disassembler.next();
if (!insn.has_value())
break;
// Prefix regions of instructions belonging to a symbol with the symbol's name.
// Separate regions of instructions belonging to distinct symbols with newlines,
// and separate regions of instructions not belonging to symbols from regions belonging to symbols with newlines.
// Interesting cases:
// - More than 1 symbol covering a region of instructions (ICF, D1/D2)
// - Symbols of size 0 that don't cover any instructions but are at an address (want to print them, separated from instructions both before and after)
// Invariant: current_symbol is the largest instruction containing insn, or it is the largest instruction that has an address less than the instruction's address.
size_t virtual_offset = file_offset + offset;
if (current_symbol < symbols.end() && !current_symbol->contains(virtual_offset)) {
if (!is_first_symbol && current_instruction_is_in_symbol) {
// The previous instruction was part of a symbol that doesn't cover the current instruction, so separate it from the current instruction with a newline.
outln();
current_instruction_is_in_symbol = (current_symbol + 1 < symbols.end() && (current_symbol + 1)->contains(virtual_offset));
}
// Try to find symbol covering current instruction, if one exists.
while (current_symbol + 1 < symbols.end() && !(current_symbol + 1)->contains(virtual_offset) && (current_symbol + 1)->address() <= virtual_offset) {
++current_symbol;
if (!is_first_symbol)
outln("\n({} ({:p}-{:p}))\n", current_symbol->name, current_symbol->address(), current_symbol->address_end());
}
while (current_symbol + 1 < symbols.end() && (current_symbol + 1)->contains(virtual_offset)) {
if (!is_first_symbol && !current_instruction_is_in_symbol)
outln();
++current_symbol;
current_instruction_is_in_symbol = true;
outln("{} ({:p}-{:p}):", current_symbol->name, current_symbol->address(), current_symbol->address_end());
}
is_first_symbol = false;
}
outln("{:p} {}", virtual_offset, insn.value().to_string(virtual_offset, symbol_provider));
}
}

View file

@ -0,0 +1,208 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <AK/Vector.h>
#include <LibCore/ElapsedTimer.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
struct Result {
u64 write_bps;
u64 read_bps;
};
static Result average_result(const Vector<Result>& results)
{
Result average;
for (auto& res : results) {
average.write_bps += res.write_bps;
average.read_bps += res.read_bps;
}
average.write_bps /= results.size();
average.read_bps /= results.size();
return average;
}
static void exit_with_usage(int rc)
{
fprintf(stderr, "Usage: disk_benchmark [-h] [-d directory] [-t time_per_benchmark] [-f file_size1,file_size2,...] [-b block_size1,block_size2,...]\n");
exit(rc);
}
static Result benchmark(const String& filename, int file_size, int block_size, ByteBuffer& buffer, bool allow_cache);
int main(int argc, char** argv)
{
char* directory = strdup(".");
int time_per_benchmark = 10;
Vector<int> file_sizes;
Vector<int> block_sizes;
bool allow_cache = false;
int opt;
while ((opt = getopt(argc, argv, "chd:t:f:b:")) != -1) {
switch (opt) {
case 'h':
exit_with_usage(0);
break;
case 'c':
allow_cache = true;
break;
case 'd':
directory = strdup(optarg);
break;
case 't':
time_per_benchmark = atoi(optarg);
break;
case 'f':
for (auto size : String(optarg).split(','))
file_sizes.append(atoi(size.characters()));
break;
case 'b':
for (auto size : String(optarg).split(','))
block_sizes.append(atoi(size.characters()));
break;
}
}
if (file_sizes.size() == 0) {
file_sizes = { 131072, 262144, 524288, 1048576, 5242880 };
}
if (block_sizes.size() == 0) {
block_sizes = { 8192, 32768, 65536 };
}
umask(0644);
auto filename = String::format("%s/disk_benchmark.tmp", directory);
for (auto file_size : file_sizes) {
for (auto block_size : block_sizes) {
if (block_size > file_size)
continue;
auto buffer = ByteBuffer::create_uninitialized(block_size);
Vector<Result> results;
printf("Running: file_size=%d block_size=%d\n", file_size, block_size);
Core::ElapsedTimer timer;
timer.start();
while (timer.elapsed() < time_per_benchmark * 1000) {
printf(".");
fflush(stdout);
results.append(benchmark(filename, file_size, block_size, buffer, allow_cache));
usleep(100);
}
auto average = average_result(results);
printf("\nFinished: runs=%zu time=%dms write_bps=%llu read_bps=%llu\n", results.size(), timer.elapsed(), average.write_bps, average.read_bps);
sleep(1);
}
}
if (isatty(0)) {
printf("Press any key to exit...\n");
fgetc(stdin);
}
}
Result benchmark(const String& filename, int file_size, int block_size, ByteBuffer& buffer, bool allow_cache)
{
int flags = O_CREAT | O_TRUNC | O_RDWR;
if (!allow_cache)
flags |= O_DIRECT;
int fd = open(filename.characters(), flags, 0644);
if (fd == -1) {
perror("open");
exit(1);
}
auto cleanup_and_exit = [fd, filename]() {
close(fd);
unlink(filename.characters());
exit(1);
};
Result res;
Core::ElapsedTimer timer;
timer.start();
int nwrote = 0;
for (int j = 0; j < file_size; j += block_size) {
int n = write(fd, buffer.data(), block_size);
if (n < 0) {
perror("write");
cleanup_and_exit();
}
nwrote += n;
}
res.write_bps = (u64)(timer.elapsed() ? (file_size / timer.elapsed()) : file_size) * 1000;
if (lseek(fd, 0, SEEK_SET) < 0) {
perror("lseek");
cleanup_and_exit();
}
timer.start();
int nread = 0;
while (nread < file_size) {
int n = read(fd, buffer.data(), block_size);
if (n < 0) {
perror("read");
cleanup_and_exit();
}
nread += n;
}
res.read_bps = (u64)(timer.elapsed() ? (file_size / timer.elapsed()) : file_size) * 1000;
if (close(fd) != 0) {
perror("close");
cleanup_and_exit();
}
if (unlink(filename.characters()) != 0) {
perror("unlink");
cleanup_and_exit();
}
return res;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <LibCore/File.h>
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/proc/dmesg", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
auto f = Core::File::construct("/proc/dmesg");
if (!f->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "open: failed to open /proc/dmesg: %s\n", f->error_string());
return 1;
}
const auto& b = f->read_all();
for (size_t i = 0; i < b.size(); ++i)
putchar(b[i]);
return 0;
}

205
Userland/Utilities/du.cpp Normal file
View file

@ -0,0 +1,205 @@
/*
* Copyright (c) 2020, Fei Wu <f.eiwu@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/LexicalPath.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibCore/Object.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
struct DuOption {
enum class TimeType {
NotUsed,
Modification,
Access,
Status
};
bool all = false;
bool apparent_size = false;
int threshold = 0;
TimeType time_type = TimeType::NotUsed;
Vector<String> excluded_patterns;
};
static int parse_args(int argc, char** argv, Vector<String>& files, DuOption& du_option, int& max_depth);
static int print_space_usage(const String& path, const DuOption& du_option, int max_depth);
int main(int argc, char** argv)
{
Vector<String> files;
DuOption du_option;
int max_depth = INT_MAX;
if (parse_args(argc, argv, files, du_option, max_depth))
return 1;
for (const auto& file : files) {
if (print_space_usage(file, du_option, max_depth))
return 1;
}
return 0;
}
int parse_args(int argc, char** argv, Vector<String>& files, DuOption& du_option, int& max_depth)
{
bool summarize = false;
const char* pattern = nullptr;
const char* exclude_from = nullptr;
Vector<const char*> files_to_process;
Core::ArgsParser::Option time_option {
true,
"Show time of type time-type of any file in the directory, or any of its subdirectories. "
"Available choices: mtime, modification, ctime, status, use, atime, access",
"time",
0,
"time-type",
[&du_option](const char* s) {
if (!strcmp(s, "mtime") || !strcmp(s, "modification"))
du_option.time_type = DuOption::TimeType::Modification;
else if (!strcmp(s, "ctime") || !strcmp(s, "status") || !strcmp(s, "use"))
du_option.time_type = DuOption::TimeType::Status;
else if (!strcmp(s, "atime") || !strcmp(s, "access"))
du_option.time_type = DuOption::TimeType::Access;
else
return false;
return true;
}
};
Core::ArgsParser args_parser;
args_parser.set_general_help("Display actual or apparent disk usage of files or directories.");
args_parser.add_option(du_option.all, "Write counts for all files, not just directories", "all", 'a');
args_parser.add_option(du_option.apparent_size, "Print apparent sizes, rather than disk usage", "apparent-size", 0);
args_parser.add_option(max_depth, "Print the total for a directory or file only if it is N or fewer levels below the command line argument", "max-depth", 'd', "N");
args_parser.add_option(summarize, "Display only a total for each argument", "summarize", 's');
args_parser.add_option(du_option.threshold, "Exclude entries smaller than size if positive, or entries greater than size if negative", "threshold", 't', "size");
args_parser.add_option(move(time_option));
args_parser.add_option(pattern, "Exclude files that match pattern", "exclude", 0, "pattern");
args_parser.add_option(exclude_from, "Exclude files that match any pattern in file", "exclude_from", 'X', "file");
args_parser.add_positional_argument(files_to_process, "File to process", "file", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (summarize)
max_depth = 0;
if (pattern)
du_option.excluded_patterns.append(pattern);
if (exclude_from) {
auto file = Core::File::construct(exclude_from);
bool success = file->open(Core::IODevice::ReadOnly);
ASSERT(success);
if (const auto buff = file->read_all()) {
String patterns = String::copy(buff, Chomp);
du_option.excluded_patterns.append(patterns.split('\n'));
}
}
for (auto* file : files_to_process) {
files.append(file);
}
if (files.is_empty()) {
files.append(".");
}
return 0;
}
int print_space_usage(const String& path, const DuOption& du_option, int max_depth)
{
struct stat path_stat;
if (lstat(path.characters(), &path_stat) < 0) {
perror("lstat");
return 1;
}
if (--max_depth >= 0 && S_ISDIR(path_stat.st_mode)) {
auto di = Core::DirIterator(path, Core::DirIterator::SkipParentAndBaseDir);
if (di.has_error()) {
fprintf(stderr, "DirIterator: %s\n", di.error_string());
return 1;
}
while (di.has_next()) {
const auto child_path = di.next_full_path();
if (du_option.all || Core::File::is_directory(child_path)) {
if (print_space_usage(child_path, du_option, max_depth))
return 1;
}
}
}
const auto basename = LexicalPath(path).basename();
for (const auto& pattern : du_option.excluded_patterns) {
if (basename.matches(pattern, CaseSensitivity::CaseSensitive))
return 0;
}
long long size = path_stat.st_size;
if (du_option.apparent_size) {
const auto block_size = 512;
size = path_stat.st_blocks * block_size;
}
if ((du_option.threshold > 0 && size < du_option.threshold) || (du_option.threshold < 0 && size > -du_option.threshold))
return 0;
const long long block_size = 1024;
size = size / block_size + (size % block_size != 0);
if (du_option.time_type == DuOption::TimeType::NotUsed)
printf("%lld\t%s\n", size, path.characters());
else {
auto time = path_stat.st_mtime;
switch (du_option.time_type) {
case DuOption::TimeType::Access:
time = path_stat.st_atime;
break;
case DuOption::TimeType::Status:
time = path_stat.st_ctime;
default:
break;
}
const auto formatted_time = Core::DateTime::from_timestamp(time).to_string();
printf("%lld\t%s\t%s\n", size, formatted_time.characters(), path.characters());
}
return 0;
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
Vector<const char*> values;
bool no_trailing_newline = false;
Core::ArgsParser args_parser;
args_parser.add_option(no_trailing_newline, "Do not output a trailing newline", nullptr, 'n');
args_parser.add_positional_argument(values, "Values to print out", "string", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
for (size_t i = 0; i < values.size(); ++i) {
fputs(values[i], stdout);
if (i != values.size() - 1)
fputc(' ', stdout);
}
if (!no_trailing_newline)
printf("\n");
return 0;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/DirIterator.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath exec", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* filename = nullptr;
for (int idx = 1; idx < argc; ++idx) {
if (idx == 1) {
if (StringView { argv[idx] } == "-i" || StringView { argv[idx] } == "--ignore-environment") {
clearenv();
continue;
}
}
if (StringView { argv[idx] }.contains('=')) {
putenv(argv[idx]);
} else {
filename = argv[idx];
argv += idx;
break;
}
}
if (filename == nullptr) {
for (auto entry = environ; *entry != nullptr; ++entry)
printf("%s\n", *entry);
return 0;
}
String filepath = Core::find_executable_in_path(filename);
if (filepath.is_null()) {
warnln("no {} in path", filename);
return 1;
}
execv(filepath.characters(), argv);
perror("execv");
return 1;
}

620
Userland/Utilities/expr.cpp Normal file
View file

@ -0,0 +1,620 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/GenericLexer.h>
#include <AK/LogStream.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Queue.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibRegex/Regex.h>
#include <stdio.h>
#include <unistd.h>
static void print_help_and_exit()
{
outln(R"(
Usage: expr EXPRESSION
expr [--help]
Print the value of EXPRESSION to standard output.)");
exit(0);
}
template<typename... Args>
[[noreturn]] void fail(Args&&... args)
{
warn("ERROR: \e[31m");
warnln(args...);
warn("\e[0m");
exit(1);
}
class Expression {
public:
enum Precedence {
Or,
And,
Comp,
ArithS,
ArithM,
StringO,
Paren,
};
static NonnullOwnPtr<Expression> parse(Queue<StringView>& args, Precedence prec = Or);
enum class Type {
Integer,
String,
};
virtual bool truth() const = 0;
virtual int integer() const = 0;
virtual String string() const = 0;
virtual Type type() const = 0;
virtual ~Expression() { }
};
class ValueExpression : public Expression {
public:
ValueExpression(int v)
: as_integer(v)
, m_type(Type::Integer)
{
}
ValueExpression(String&& v)
: as_string(move(v))
, m_type(Type::String)
{
}
virtual ~ValueExpression() { }
private:
virtual bool truth() const override
{
if (m_type == Type::String)
return !as_string.is_empty();
return integer() != 0;
}
virtual int integer() const override
{
switch (m_type) {
case Type::Integer:
return as_integer;
case Type::String:
if (auto converted = as_string.to_int(); converted.has_value())
return converted.value();
fail("Not an integer: '{}'", as_string);
}
ASSERT_NOT_REACHED();
}
virtual String string() const override
{
switch (m_type) {
case Type::Integer:
return String::formatted("{}", as_integer);
case Type::String:
return as_string;
}
ASSERT_NOT_REACHED();
}
virtual Type type() const override { return m_type; }
union {
int as_integer;
String as_string;
};
Type m_type { Type::String };
};
class BooleanExpression : public Expression {
public:
enum class BooleanOperator {
And,
Or,
};
static BooleanOperator op_from(const StringView& sv)
{
if (sv == "&")
return BooleanOperator::And;
return BooleanOperator::Or;
}
BooleanExpression(BooleanOperator op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
: m_op(op)
, m_left(move(left))
, m_right(move(right))
{
if (m_op == BooleanOperator::Or)
m_left_truth = m_left->truth();
else
m_right_truth = m_right->truth();
}
private:
virtual bool truth() const override
{
if (m_op == BooleanOperator::Or)
return m_left_truth ? true : m_right->truth();
return m_right_truth ? m_left->truth() : false;
}
virtual int integer() const override
{
switch (m_op) {
case BooleanOperator::And:
if (m_right_truth)
return m_left->integer();
return 0;
case BooleanOperator::Or:
if (m_left_truth)
return m_left->integer();
return m_right->integer();
}
ASSERT_NOT_REACHED();
}
virtual String string() const override
{
switch (m_op) {
case BooleanOperator::And:
if (m_right_truth)
return m_left->string();
return "0";
case BooleanOperator::Or:
if (m_left_truth)
return m_left->string();
return m_right->string();
}
ASSERT_NOT_REACHED();
}
virtual Type type() const override
{
switch (m_op) {
case BooleanOperator::And:
if (m_right_truth)
return m_left->type();
return m_right->type();
case BooleanOperator::Or:
if (m_left_truth)
return m_left->type();
return m_right->type();
}
ASSERT_NOT_REACHED();
}
BooleanOperator m_op { BooleanOperator::And };
NonnullOwnPtr<Expression> m_left, m_right;
bool m_left_truth { false }, m_right_truth { false };
};
class ComparisonExpression : public Expression {
public:
enum class ComparisonOperation {
Less,
LessEq,
Eq,
Neq,
GreaterEq,
Greater,
};
static ComparisonOperation op_from(const StringView& sv)
{
if (sv == "<")
return ComparisonOperation::Less;
if (sv == "<=")
return ComparisonOperation::LessEq;
if (sv == "=")
return ComparisonOperation::Eq;
if (sv == "!=")
return ComparisonOperation::Neq;
if (sv == ">=")
return ComparisonOperation::GreaterEq;
return ComparisonOperation::Greater;
}
ComparisonExpression(ComparisonOperation op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
: m_op(op)
, m_left(move(left))
, m_right(move(right))
{
}
private:
template<typename T>
bool compare(const T& left, const T& right) const
{
switch (m_op) {
case ComparisonOperation::Less:
return left < right;
case ComparisonOperation::LessEq:
return left == right || left < right;
case ComparisonOperation::Eq:
return left == right;
case ComparisonOperation::Neq:
return left != right;
case ComparisonOperation::GreaterEq:
return !(left < right);
case ComparisonOperation::Greater:
return left != right && !(left < right);
}
ASSERT_NOT_REACHED();
}
virtual bool truth() const override
{
switch (m_left->type()) {
case Type::Integer:
return compare(m_left->integer(), m_right->integer());
case Type::String:
return compare(m_left->string(), m_right->string());
}
ASSERT_NOT_REACHED();
}
virtual int integer() const override { return truth(); }
virtual String string() const override { return truth() ? "1" : "0"; }
virtual Type type() const override { return Type::Integer; }
ComparisonOperation m_op { ComparisonOperation::Less };
NonnullOwnPtr<Expression> m_left, m_right;
};
class ArithmeticExpression : public Expression {
public:
enum class ArithmeticOperation {
Sum,
Difference,
Product,
Quotient,
Remainder,
};
static ArithmeticOperation op_from(const StringView& sv)
{
if (sv == "+")
return ArithmeticOperation::Sum;
if (sv == "-")
return ArithmeticOperation::Difference;
if (sv == "*")
return ArithmeticOperation::Product;
if (sv == "/")
return ArithmeticOperation::Quotient;
return ArithmeticOperation::Remainder;
}
ArithmeticExpression(ArithmeticOperation op, NonnullOwnPtr<Expression>&& left, NonnullOwnPtr<Expression>&& right)
: m_op(op)
, m_left(move(left))
, m_right(move(right))
{
}
private:
virtual bool truth() const override
{
switch (m_op) {
case ArithmeticOperation::Sum:
return m_left->truth() || m_right->truth();
default:
return integer() != 0;
}
}
virtual int integer() const override
{
auto right = m_right->integer();
if (right == 0) {
if (m_op == ArithmeticOperation::Product)
return 0;
if (m_op == ArithmeticOperation::Quotient || m_op == ArithmeticOperation::Remainder)
fail("Division by zero");
}
auto left = m_left->integer();
switch (m_op) {
case ArithmeticOperation::Product:
return right * left;
case ArithmeticOperation::Sum:
return right + left;
case ArithmeticOperation::Difference:
return left - right;
case ArithmeticOperation::Quotient:
return left / right;
case ArithmeticOperation::Remainder:
return left % right;
}
ASSERT_NOT_REACHED();
}
virtual String string() const override
{
return String::formatted("{}", integer());
}
virtual Type type() const override
{
return Type::Integer;
}
ArithmeticOperation m_op { ArithmeticOperation::Sum };
NonnullOwnPtr<Expression> m_left, m_right;
};
class StringExpression : public Expression {
public:
enum class StringOperation {
Substring,
Index,
Length,
Match,
};
StringExpression(StringOperation op, NonnullOwnPtr<Expression> string, OwnPtr<Expression> pos_or_chars = {}, OwnPtr<Expression> length = {})
: m_op(op)
, m_str(move(string))
, m_pos_or_chars(move(pos_or_chars))
, m_length(move(length))
{
}
private:
virtual bool truth() const override { return integer() != 0; }
virtual int integer() const override
{
if (m_op == StringOperation::Substring || m_op == StringOperation::Match) {
auto substr = string();
if (auto integer = substr.to_int(); integer.has_value())
return integer.value();
else
fail("Not an integer: '{}'", substr);
}
if (m_op == StringOperation::Index) {
if (auto idx = m_str->string().index_of(m_pos_or_chars->string()); idx.has_value())
return idx.value() + 1;
return 0;
}
if (m_op == StringOperation::Length)
return m_str->string().length();
ASSERT_NOT_REACHED();
}
static auto safe_substring(const String& str, int start, int length)
{
if (start < 1 || (size_t)start > str.length())
fail("Index out of range");
--start;
if (str.length() - start < (size_t)length)
fail("Index out of range");
return str.substring(start, length);
}
virtual String string() const override
{
if (m_op == StringOperation::Substring)
return safe_substring(m_str->string(), m_pos_or_chars->integer(), m_length->integer());
if (m_op == StringOperation::Match) {
auto match = m_compiled_regex->match(m_str->string(), PosixFlags::Global);
if (m_compiled_regex->parser_result.capture_groups_count == 0) {
if (!match.success)
return "0";
size_t count = 0;
for (auto& m : match.matches)
count += m.view.length();
return String::number(count);
} else {
if (!match.success)
return "";
StringBuilder result;
for (auto& m : match.capture_group_matches) {
for (auto& e : m)
result.append(e.view.to_string());
}
return result.build();
}
}
return String::number(integer());
}
virtual Type type() const override
{
if (m_op == StringOperation::Substring)
return Type::String;
if (m_op == StringOperation::Match) {
if (!m_pos_or_chars)
fail("'match' expects a string pattern");
ensure_regex();
if (m_compiled_regex->parser_result.capture_groups_count == 0)
return Type::Integer;
return Type::String;
}
return Type::Integer;
}
void ensure_regex() const
{
if (!m_compiled_regex)
m_compiled_regex = make<regex::Regex<PosixExtended>>(m_pos_or_chars->string());
}
StringOperation m_op { StringOperation::Substring };
NonnullOwnPtr<Expression> m_str;
OwnPtr<Expression> m_pos_or_chars, m_length;
mutable OwnPtr<regex::Regex<PosixExtended>> m_compiled_regex;
};
NonnullOwnPtr<Expression> Expression::parse(Queue<StringView>& args, Precedence prec)
{
switch (prec) {
case Or: {
auto left = parse(args, And);
while (!args.is_empty() && args.head() == "|") {
args.dequeue();
auto right = parse(args, And);
left = make<BooleanExpression>(BooleanExpression::BooleanOperator::Or, move(left), move(right));
}
return left;
}
case And: {
auto left = parse(args, Comp);
while (!args.is_empty() && args.head() == "&") {
args.dequeue();
auto right = parse(args, Comp);
left = make<BooleanExpression>(BooleanExpression::BooleanOperator::And, move(left), move(right));
}
return left;
}
case Comp: {
auto left = parse(args, ArithS);
while (!args.is_empty() && args.head().is_one_of("<", "<=", "=", "!=", "=>", ">")) {
auto op = args.dequeue();
auto right = parse(args, ArithM);
left = make<ComparisonExpression>(ComparisonExpression::op_from(op), move(left), move(right));
}
return left;
}
case ArithS: {
auto left = parse(args, ArithM);
while (!args.is_empty() && args.head().is_one_of("+", "-")) {
auto op = args.dequeue();
auto right = parse(args, ArithM);
left = make<ArithmeticExpression>(ArithmeticExpression::op_from(op), move(left), move(right));
}
return left;
}
case ArithM: {
auto left = parse(args, StringO);
while (!args.is_empty() && args.head().is_one_of("*", "/", "%")) {
auto op = args.dequeue();
auto right = parse(args, StringO);
left = make<ArithmeticExpression>(ArithmeticExpression::op_from(op), move(left), move(right));
}
return left;
}
case StringO: {
if (args.is_empty())
fail("Expected a term");
OwnPtr<Expression> left;
while (!args.is_empty()) {
auto& op = args.head();
if (op == "+") {
args.dequeue();
left = make<ValueExpression>(args.dequeue());
} else if (op == "substr") {
args.dequeue();
auto str = parse(args, Paren);
auto pos = parse(args, Paren);
auto len = parse(args, Paren);
left = make<StringExpression>(StringExpression::StringOperation::Substring, move(str), move(pos), move(len));
} else if (op == "index") {
args.dequeue();
auto str = parse(args, Paren);
auto chars = parse(args, Paren);
left = make<StringExpression>(StringExpression::StringOperation::Index, move(str), move(chars));
} else if (op == "match") {
args.dequeue();
auto str = parse(args, Paren);
auto pattern = parse(args, Paren);
left = make<StringExpression>(StringExpression::StringOperation::Match, move(str), move(pattern));
} else if (op == "length") {
args.dequeue();
auto str = parse(args, Paren);
left = make<StringExpression>(StringExpression::StringOperation::Length, move(str));
} else if (!left) {
left = parse(args, Paren);
}
if (!args.is_empty() && args.head() == ":") {
args.dequeue();
auto right = parse(args, Paren);
left = make<StringExpression>(StringExpression::StringOperation::Match, left.release_nonnull(), move(right));
} else {
return left.release_nonnull();
}
}
return left.release_nonnull();
}
case Paren: {
if (args.is_empty())
fail("Expected a term");
if (args.head() == "(") {
args.dequeue();
auto expr = parse(args);
if (args.head() != ")")
fail("Expected a close paren");
args.dequeue();
return expr;
}
return make<ValueExpression>(args.dequeue());
}
}
fail("Invalid expression");
}
int main(int argc, char** argv)
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
if ((argc == 2 && StringView { "--help" } == argv[1]) || argc == 1)
print_help_and_exit();
Queue<StringView> args;
for (int i = 1; i < argc; ++i)
args.enqueue(argv[i]);
auto expression = Expression::parse(args);
if (!args.is_empty())
fail("Extra tokens at the end of the expression");
switch (expression->type()) {
case Expression::Type::Integer:
outln("{}", expression->integer());
break;
case Expression::Type::String:
outln("{}", expression->string());
break;
}
return 0;
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
int main(int, char**)
{
return 1;
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc < 2) {
printf("usage: fgrep <str>\n");
return 0;
}
for (;;) {
char buf[4096];
auto* str = fgets(buf, sizeof(buf), stdin);
if (str && strstr(str, argv[1]))
write(1, buf, strlen(buf));
if (feof(stdin))
return 0;
ASSERT(str);
}
return 0;
}

492
Userland/Utilities/find.cpp Normal file
View file

@ -0,0 +1,492 @@
/*
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/OwnPtr.h>
#include <AK/Vector.h>
#include <LibCore/DirIterator.h>
#include <getopt.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
bool g_follow_symlinks = false;
bool g_there_was_an_error = false;
bool g_have_seen_action_command = false;
[[noreturn]] static void fatal_error(const char* format, ...)
{
fputs("\033[31m", stderr);
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fputs("\033[0m\n", stderr);
exit(1);
}
class Command {
public:
virtual ~Command() { }
virtual bool evaluate(const char* file_path) const = 0;
};
class StatCommand : public Command {
public:
virtual bool evaluate(const struct stat&) const = 0;
private:
virtual bool evaluate(const char* file_path) const override
{
struct stat stat;
auto stat_func = g_follow_symlinks ? ::stat : ::lstat;
int rc = stat_func(file_path, &stat);
if (rc < 0) {
perror(file_path);
g_there_was_an_error = true;
return false;
}
return evaluate(stat);
}
};
class TypeCommand final : public StatCommand {
public:
TypeCommand(const char* arg)
{
StringView type = arg;
if (type.length() != 1 || !StringView("bcdlpfs").contains(type[0]))
fatal_error("Invalid mode: \033[1m%s", arg);
m_type = type[0];
}
private:
virtual bool evaluate(const struct stat& stat) const override
{
auto type = stat.st_mode;
switch (m_type) {
case 'b':
return S_ISBLK(type);
case 'c':
return S_ISCHR(type);
case 'd':
return S_ISDIR(type);
case 'l':
return S_ISLNK(type);
case 'p':
return S_ISFIFO(type);
case 'f':
return S_ISREG(type);
case 's':
return S_ISSOCK(type);
default:
// We've verified this is a correct character before.
ASSERT_NOT_REACHED();
}
}
char m_type { 0 };
};
class LinksCommand final : public StatCommand {
public:
LinksCommand(const char* arg)
{
auto number = StringView(arg).to_uint();
if (!number.has_value())
fatal_error("Invalid number: \033[1m%s", arg);
m_links = number.value();
}
private:
virtual bool evaluate(const struct stat& stat) const override
{
return stat.st_nlink == m_links;
}
nlink_t m_links { 0 };
};
class UserCommand final : public StatCommand {
public:
UserCommand(const char* arg)
{
if (struct passwd* passwd = getpwnam(arg)) {
m_uid = passwd->pw_uid;
} else {
// Attempt to parse it as decimal UID.
auto number = StringView(arg).to_uint();
if (!number.has_value())
fatal_error("Invalid user: \033[1m%s", arg);
m_uid = number.value();
}
}
private:
virtual bool evaluate(const struct stat& stat) const override
{
return stat.st_uid == m_uid;
}
uid_t m_uid { 0 };
};
class GroupCommand final : public StatCommand {
public:
GroupCommand(const char* arg)
{
if (struct group* gr = getgrnam(arg)) {
m_gid = gr->gr_gid;
} else {
// Attempt to parse it as decimal GID.
auto number = StringView(arg).to_int();
if (!number.has_value())
fatal_error("Invalid group: \033[1m%s", arg);
m_gid = number.value();
}
}
private:
virtual bool evaluate(const struct stat& stat) const override
{
return stat.st_gid == m_gid;
}
gid_t m_gid { 0 };
};
class SizeCommand final : public StatCommand {
public:
SizeCommand(const char* arg)
{
StringView view = arg;
if (view.ends_with('c')) {
m_is_bytes = true;
view = view.substring_view(0, view.length() - 1);
}
auto number = view.to_uint();
if (!number.has_value())
fatal_error("Invalid size: \033[1m%s", arg);
m_size = number.value();
}
private:
virtual bool evaluate(const struct stat& stat) const override
{
if (m_is_bytes)
return stat.st_size == m_size;
auto size_divided_by_512_rounded_up = (stat.st_size + 511) / 512;
return size_divided_by_512_rounded_up == m_size;
}
off_t m_size { 0 };
bool m_is_bytes { false };
};
class NameCommand : public Command {
public:
NameCommand(const char* pattern, CaseSensitivity case_sensitivity)
: m_pattern(pattern)
, m_case_sensitivity(case_sensitivity)
{
}
private:
virtual bool evaluate(const char* file_path) const override
{
LexicalPath path { file_path };
return path.basename().matches(m_pattern, m_case_sensitivity);
}
StringView m_pattern;
CaseSensitivity m_case_sensitivity { CaseSensitivity::CaseSensitive };
};
class PrintCommand final : public Command {
public:
PrintCommand(char terminator = '\n')
: m_terminator(terminator)
{
}
private:
virtual bool evaluate(const char* file_path) const override
{
printf("%s%c", file_path, m_terminator);
return true;
}
char m_terminator { '\n' };
};
class ExecCommand final : public Command {
public:
ExecCommand(Vector<char*>&& argv)
: m_argv(move(argv))
{
}
private:
virtual bool evaluate(const char* file_path) const override
{
pid_t pid = fork();
if (pid < 0) {
perror("fork");
g_there_was_an_error = true;
return false;
} else if (pid == 0) {
// Replace any occurrences of "{}" with the path. Since we're in the
// child and going to exec real soon, let's just const_cast away the
// constness.
auto argv = const_cast<Vector<char*>&>(m_argv);
for (auto& arg : argv) {
if (StringView(arg) == "{}")
arg = const_cast<char*>(file_path);
}
argv.append(nullptr);
execvp(m_argv[0], argv.data());
perror("execvp");
exit(1);
} else {
int status;
int rc = waitpid(pid, &status, 0);
if (rc < 0) {
perror("waitpid");
g_there_was_an_error = true;
return false;
}
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
}
Vector<char*> m_argv;
};
class AndCommand final : public Command {
public:
AndCommand(NonnullOwnPtr<Command>&& lhs, NonnullOwnPtr<Command>&& rhs)
: m_lhs(move(lhs))
, m_rhs(move(rhs))
{
}
private:
virtual bool evaluate(const char* file_path) const override
{
return m_lhs->evaluate(file_path) && m_rhs->evaluate(file_path);
}
NonnullOwnPtr<Command> m_lhs;
NonnullOwnPtr<Command> m_rhs;
};
class OrCommand final : public Command {
public:
OrCommand(NonnullOwnPtr<Command>&& lhs, NonnullOwnPtr<Command>&& rhs)
: m_lhs(move(lhs))
, m_rhs(move(rhs))
{
}
private:
virtual bool evaluate(const char* file_path) const override
{
return m_lhs->evaluate(file_path) || m_rhs->evaluate(file_path);
}
NonnullOwnPtr<Command> m_lhs;
NonnullOwnPtr<Command> m_rhs;
};
static OwnPtr<Command> parse_complex_command(char* argv[]);
// Parse a simple command starting at optind; leave optind at its the last
// argument. Return nullptr if we reach the end of arguments.
static OwnPtr<Command> parse_simple_command(char* argv[])
{
StringView arg = argv[optind];
if (arg.is_null()) {
return {};
} else if (arg == "(") {
optind++;
auto command = parse_complex_command(argv);
if (command && argv[optind] && StringView(argv[++optind]) == ")")
return command;
fatal_error("Unmatched \033[1m(");
} else if (arg == "-type") {
return make<TypeCommand>(argv[++optind]);
} else if (arg == "-links") {
return make<LinksCommand>(argv[++optind]);
} else if (arg == "-user") {
return make<UserCommand>(argv[++optind]);
} else if (arg == "-group") {
return make<GroupCommand>(argv[++optind]);
} else if (arg == "-size") {
return make<SizeCommand>(argv[++optind]);
} else if (arg == "-name") {
return make<NameCommand>(argv[++optind], CaseSensitivity::CaseSensitive);
} else if (arg == "-iname") {
return make<NameCommand>(argv[++optind], CaseSensitivity::CaseInsensitive);
} else if (arg == "-print") {
g_have_seen_action_command = true;
return make<PrintCommand>();
} else if (arg == "-print0") {
g_have_seen_action_command = true;
return make<PrintCommand>(0);
} else if (arg == "-exec") {
g_have_seen_action_command = true;
Vector<char*> command_argv;
while (argv[++optind] && StringView(argv[optind]) != ";")
command_argv.append(argv[optind]);
return make<ExecCommand>(move(command_argv));
} else {
fatal_error("Unsupported command \033[1m%s", argv[optind]);
}
}
static OwnPtr<Command> parse_complex_command(char* argv[])
{
auto command = parse_simple_command(argv);
while (command && argv[optind] && argv[optind + 1]) {
StringView arg = argv[++optind];
enum { And,
Or } binary_operation
= And;
if (arg == "-a") {
optind++;
binary_operation = And;
} else if (arg == "-o") {
optind++;
binary_operation = Or;
} else if (arg == ")") {
// Ooops, looked too far.
optind--;
return command;
} else {
// Juxtaposition is an And too, and there's nothing to skip.
binary_operation = And;
}
auto rhs = parse_complex_command(argv);
if (!rhs)
fatal_error("Missing right-hand side");
if (binary_operation == And)
command = make<AndCommand>(command.release_nonnull(), rhs.release_nonnull());
else
command = make<OrCommand>(command.release_nonnull(), rhs.release_nonnull());
}
return command;
}
static NonnullOwnPtr<Command> parse_all_commands(char* argv[])
{
auto command = parse_complex_command(argv);
if (g_have_seen_action_command) {
ASSERT(command);
return command.release_nonnull();
}
if (!command) {
return make<PrintCommand>();
}
return make<AndCommand>(command.release_nonnull(), make<PrintCommand>());
}
static const char* parse_options(int argc, char* argv[])
{
// Sadly, we can't use Core::ArgsParser, because find accepts arguments in
// an extremely unusual format. We're going to try to use getopt(), though.
opterr = 0;
while (true) {
int opt = getopt(argc, argv, "+L");
switch (opt) {
case -1: {
// No more options.
StringView arg = argv[optind];
if (!arg.is_null() && !arg.starts_with('-')) {
// It's our root path!
return argv[optind++];
} else {
// It's a part of the script, and our root path is the current
// directory by default.
return ".";
}
}
case '?':
// Some error. Most likely, it's getopt() getting confused about
// what it thought was an option, but is actually a command. Return
// the default path, and hope the command parsing logic deals with
// this.
return ".";
case 'L':
g_follow_symlinks = true;
break;
default:
ASSERT_NOT_REACHED();
}
}
}
static void walk_tree(const char* root_path, Command& command)
{
command.evaluate(root_path);
Core::DirIterator dir_iterator(root_path, Core::DirIterator::SkipParentAndBaseDir);
if (dir_iterator.has_error() && dir_iterator.error() == ENOTDIR)
return;
while (dir_iterator.has_next())
walk_tree(dir_iterator.next_full_path().characters(), command);
if (dir_iterator.has_error()) {
fprintf(stderr, "%s: %s\n", root_path, dir_iterator.error_string());
g_there_was_an_error = true;
}
}
int main(int argc, char* argv[])
{
auto root_path = parse_options(argc, argv);
auto command = parse_all_commands(argv);
walk_tree(root_path, *command);
return g_there_was_an_error ? 1 : 0;
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc < 3) {
printf("usage: flock <path> <command...>\n");
return 0;
}
pid_t child_pid;
if ((errno = posix_spawnp(&child_pid, argv[2], nullptr, nullptr, &argv[2], environ))) {
perror("posix_spawn");
return 1;
}
int status;
if (waitpid(child_pid, &status, 0) < 0) {
perror("waitpid");
return 1;
}
return WEXITSTATUS(status);
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Assertions.h>
#include <AK/ByteBuffer.h>
#include <AK/Demangle.h>
#include <AK/HashMap.h>
#include <AK/LogStream.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/StringBuilder.h>
#include <AK/kmalloc.h>
#include <Kernel/API/Syscall.h>
#include <LibC/sys/arch/i386/regs.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibDebug/DebugSession.h>
#include <LibELF/Image.h>
#include <LibX86/Disassembler.h>
#include <LibX86/Instruction.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static OwnPtr<Debug::DebugSession> g_debug_session;
static bool g_should_output_color = false;
static void handle_sigint(int)
{
printf("Debugger: SIGINT\n");
// The destructor of DebugSession takes care of detaching
g_debug_session = nullptr;
}
static void print_function_call(String function_name, size_t depth)
{
for (size_t i = 0; i < depth; ++i) {
out(" ");
}
outln("=> {}", function_name);
}
static void print_syscall(PtraceRegisters& regs, size_t depth)
{
for (size_t i = 0; i < depth; ++i) {
printf(" ");
}
const char* begin_color = g_should_output_color ? "\033[34;1m" : "";
const char* end_color = g_should_output_color ? "\033[0m" : "";
outln("=> {}SC_{}(0x{:x}, 0x{:x}, 0x{:x}){}",
begin_color,
Syscall::to_string((Syscall::Function)regs.eax),
regs.edx,
regs.ecx,
regs.ebx,
end_color);
}
static NonnullOwnPtr<HashMap<void*, X86::Instruction>> instrument_code()
{
auto instrumented = make<HashMap<void*, X86::Instruction>>();
g_debug_session->for_each_loaded_library([&](const Debug::DebugSession::LoadedLibrary& lib) {
lib.debug_info->elf().for_each_section_of_type(SHT_PROGBITS, [&](const ELF::Image::Section& section) {
if (section.name() != ".text")
return IterationDecision::Continue;
X86::SimpleInstructionStream stream((const u8*)((u32)lib.file->data() + section.offset()), section.size());
X86::Disassembler disassembler(stream);
for (;;) {
auto offset = stream.offset();
void* instruction_address = (void*)(section.address() + offset + lib.base_address);
auto insn = disassembler.next();
if (!insn.has_value())
break;
if (insn.value().mnemonic() == "RET" || insn.value().mnemonic() == "CALL") {
g_debug_session->insert_breakpoint(instruction_address);
instrumented->set(instruction_address, insn.value());
}
}
return IterationDecision::Continue;
});
return IterationDecision::Continue;
});
return instrumented;
}
int main(int argc, char** argv)
{
if (pledge("stdio proc exec rpath sigaction ptrace", nullptr) < 0) {
perror("pledge");
return 1;
}
if (isatty(STDOUT_FILENO))
g_should_output_color = true;
const char* command = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(command,
"The program to be traced, along with its arguments",
"program", Core::ArgsParser::Required::Yes);
args_parser.parse(argc, argv);
auto result = Debug::DebugSession::exec_and_attach(command);
if (!result) {
warnln("Failed to start debugging session for: \"{}\"", command);
exit(1);
}
g_debug_session = result.release_nonnull();
auto instrumented = instrument_code();
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, nullptr);
size_t depth = 0;
bool new_function = true;
g_debug_session->run(Debug::DebugSession::DesiredInitialDebugeeState::Running, [&](Debug::DebugSession::DebugBreakReason reason, Optional<PtraceRegisters> regs) {
if (reason == Debug::DebugSession::DebugBreakReason::Exited) {
outln("Program exited.");
return Debug::DebugSession::DebugDecision::Detach;
}
if (reason == Debug::DebugSession::DebugBreakReason::Syscall) {
print_syscall(regs.value(), depth + 1);
return Debug::DebugSession::DebugDecision::ContinueBreakAtSyscall;
}
if (new_function) {
auto function_name = g_debug_session->symbolicate(regs.value().eip);
print_function_call(function_name.value().symbol, depth);
new_function = false;
return Debug::DebugSession::ContinueBreakAtSyscall;
}
auto instruction = instrumented->get((void*)regs.value().eip).value();
if (instruction.mnemonic() == "RET") {
if (depth != 0)
--depth;
return Debug::DebugSession::ContinueBreakAtSyscall;
}
// FIXME: we could miss some leaf functions that were called with a jump
ASSERT(instruction.mnemonic() == "CALL");
++depth;
new_function = true;
return Debug::DebugSession::DebugDecision::SingleStep;
});
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (c) 2021, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibGUI/GMLFormatter.h>
bool format_file(const StringView&, bool);
bool format_file(const StringView& path, bool inplace)
{
auto read_from_stdin = path == "-";
RefPtr<Core::File> file;
if (read_from_stdin) {
file = Core::File::standard_input();
} else {
auto open_mode = inplace ? Core::File::ReadWrite : Core::File::ReadOnly;
auto file_or_error = Core::File::open(path, open_mode);
if (file_or_error.is_error()) {
warnln("Could not open {}: {}", path, file_or_error.error());
return false;
}
file = file_or_error.value();
}
auto formatted_gml = GUI::format_gml(file->read_all());
if (formatted_gml.is_null()) {
warnln("Failed to parse GML!");
return false;
}
if (inplace && !read_from_stdin) {
if (!file->seek(0) || !file->truncate(0)) {
warnln("Could not truncate {}: {}", path, file->error_string());
return false;
}
if (!file->write(formatted_gml)) {
warnln("Could not write to {}: {}", path, file->error_string());
return false;
}
} else {
out("{}", formatted_gml);
}
return true;
}
int main(int argc, char** argv)
{
#ifdef __serenity__
if (pledge("stdio rpath wpath cpath", nullptr) < 0) {
perror("pledge");
return 1;
}
#endif
bool inplace = false;
Vector<const char*> files;
Core::ArgsParser args_parser;
args_parser.set_general_help("Format GML files.");
args_parser.add_option(inplace, "Write formatted contents back to file rather than standard output", "inplace", 'i');
args_parser.add_positional_argument(files, "File(s) to process", "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
#ifdef __serenity__
if (!inplace) {
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
}
#endif
unsigned exit_code = 0;
if (files.is_empty())
files.append("-");
for (auto& file : files) {
if (!format_file(file, inplace))
exit_code = 1;
}
return exit_code;
}

214
Userland/Utilities/grep.cpp Normal file
View file

@ -0,0 +1,214 @@
/*
* Copyright (c) 2020, Emanuel Sprung <emanuel.sprung@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibRegex/Regex.h>
#include <stdio.h>
#include <unistd.h>
enum class BinaryFileMode {
Binary,
Text,
Skip,
};
template<typename... Ts>
void fail(StringView format, Ts... args)
{
fprintf(stderr, "\x1b[31m");
warnln(format, forward<Ts>(args)...);
fprintf(stderr, "\x1b[0m");
abort();
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
Vector<const char*> files;
bool recursive { false };
bool use_ere { true };
const char* pattern = nullptr;
BinaryFileMode binary_mode { BinaryFileMode::Binary };
bool case_insensitive = false;
Core::ArgsParser args_parser;
args_parser.add_option(recursive, "Recursively scan files starting in working directory", "recursive", 'r');
args_parser.add_option(use_ere, "Extended regular expressions (default)", "extended-regexp", 'E');
args_parser.add_option(pattern, "Pattern", "regexp", 'e', "Pattern");
args_parser.add_option(case_insensitive, "Make matches case-insensitive", nullptr, 'i');
args_parser.add_option(Core::ArgsParser::Option {
.requires_argument = true,
.help_string = "Action to take for binary files ([binary], text, skip)",
.long_name = "binary-mode",
.accept_value = [&](auto* str) {
if (StringView { "text" } == str)
binary_mode = BinaryFileMode::Text;
else if (StringView { "binary" } == str)
binary_mode = BinaryFileMode::Binary;
else if (StringView { "skip" } == str)
binary_mode = BinaryFileMode::Skip;
else
return false;
return true;
},
});
args_parser.add_option(Core::ArgsParser::Option {
.requires_argument = false,
.help_string = "Treat binary files as text (same as --binary-mode text)",
.long_name = "text",
.short_name = 'a',
.accept_value = [&](auto) {
binary_mode = BinaryFileMode::Text;
return true;
},
});
args_parser.add_option(Core::ArgsParser::Option {
.requires_argument = false,
.help_string = "Ignore binary files (same as --binary-mode skip)",
.long_name = nullptr,
.short_name = 'I',
.accept_value = [&](auto) {
binary_mode = BinaryFileMode::Skip;
return true;
},
});
args_parser.add_positional_argument(files, "File(s) to process", "file", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (!use_ere)
return 0;
// mock grep behaviour: if -e is omitted, use first positional argument as pattern
if (pattern == nullptr && files.size())
pattern = files.take_first();
PosixOptions options {};
if (case_insensitive)
options |= PosixFlags::Insensitive;
Regex<PosixExtended> re(pattern, options);
if (re.parser_result.error != Error::NoError) {
return 1;
}
auto matches = [&](StringView str, StringView filename = "", bool print_filename = false, bool is_binary = false) {
size_t last_printed_char_pos { 0 };
if (is_binary && binary_mode == BinaryFileMode::Skip)
return false;
auto result = re.match(str, PosixFlags::Global);
if (result.success) {
if (is_binary && binary_mode == BinaryFileMode::Binary) {
outln("binary file \x1B[34m{}\x1B[0m matches", filename);
} else {
if (result.matches.size() && print_filename) {
out("\x1B[34m{}:\x1B[0m", filename);
}
for (auto& match : result.matches) {
out("{}\x1B[32m{}\x1B[0m",
StringView(&str[last_printed_char_pos], match.global_offset - last_printed_char_pos),
match.view.to_string());
last_printed_char_pos = match.global_offset + match.view.length();
}
outln("{}", StringView(&str[last_printed_char_pos], str.length() - last_printed_char_pos));
}
return true;
}
return false;
};
auto handle_file = [&matches, binary_mode](StringView filename, bool print_filename) -> bool {
auto file = Core::File::construct(filename);
if (!file->open(Core::IODevice::ReadOnly)) {
warnln("Failed to open {}: {}", filename, file->error_string());
return false;
}
while (file->can_read_line()) {
auto line = file->read_line();
auto is_binary = memchr(line.characters(), 0, line.length()) != nullptr;
if (matches(line, filename, print_filename, is_binary) && is_binary && binary_mode == BinaryFileMode::Binary)
return true;
}
return true;
};
auto add_directory = [&handle_file](String base, Optional<String> recursive, auto handle_directory) -> void {
Core::DirIterator it(recursive.value_or(base), Core::DirIterator::Flags::SkipDots);
while (it.has_next()) {
auto path = it.next_full_path();
if (!Core::File::is_directory(path)) {
auto key = path.substring_view(base.length() + 1, path.length() - base.length() - 1);
handle_file(key, true);
} else {
handle_directory(base, path, handle_directory);
}
}
};
if (!files.size() && !recursive) {
auto stdin_file = Core::File::standard_input();
while (!stdin_file->eof()) {
auto line = stdin_file->read_line();
bool is_binary = line.bytes().contains_slow(0);
if (is_binary && binary_mode == BinaryFileMode::Skip)
return 1;
if (matches(line, "stdin", false, is_binary) && is_binary && binary_mode == BinaryFileMode::Binary)
return 0;
}
} else {
if (recursive) {
add_directory(".", {}, add_directory);
} else {
bool print_filename { files.size() > 1 };
for (auto& filename : files) {
if (!handle_file(filename, print_filename))
return 1;
}
}
}
return 0;
}

137
Userland/Utilities/gron.cpp Normal file
View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <LibCore/File.h>
#include <stdio.h>
#include <string.h>
static bool use_color = false;
static void print(const String& name, const JsonValue&, Vector<String>& trail);
static const char* color_name = "";
static const char* color_index = "";
static const char* color_brace = "";
static const char* color_bool = "";
static const char* color_null = "";
static const char* color_string = "";
static const char* color_off = "";
int main(int argc, char** argv)
{
if (pledge("stdio tty rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (isatty(STDOUT_FILENO))
use_color = true;
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (argc != 2 || !strcmp(argv[1], "--help")) {
fprintf(stderr, "usage: gron <file>\n");
fprintf(stderr, "Print each value in a JSON file with its fully expanded key.\n");
return 0;
}
auto file = Core::File::construct(argv[1]);
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Couldn't open %s for reading: %s\n", argv[1], file->error_string());
return 1;
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
if (use_color) {
color_name = "\033[33;1m";
color_index = "\033[35;1m";
color_brace = "\033[36m";
color_bool = "\033[32;1m";
color_string = "\033[31;1m";
color_null = "\033[34;1m";
color_off = "\033[0m";
}
Vector<String> trail;
print("json", json.value(), trail);
return 0;
}
static void print(const String& name, const JsonValue& value, Vector<String>& trail)
{
for (size_t i = 0; i < trail.size(); ++i)
printf("%s", trail[i].characters());
printf("%s%s%s = ", color_name, name.characters(), color_off);
if (value.is_object()) {
printf("%s{}%s;\n", color_brace, color_off);
trail.append(String::format("%s%s%s.", color_name, name.characters(), color_off));
value.as_object().for_each_member([&](auto& on, auto& ov) { print(on, ov, trail); });
trail.take_last();
return;
}
if (value.is_array()) {
printf("%s[]%s;\n", color_brace, color_off);
trail.append(String::format("%s%s%s", color_name, name.characters(), color_off));
for (int i = 0; i < value.as_array().size(); ++i) {
auto element_name = String::format("%s%s[%s%s%d%s%s]%s", color_off, color_brace, color_off, color_index, i, color_off, color_brace, color_off);
print(element_name, value.as_array()[i], trail);
}
trail.take_last();
return;
}
switch (value.type()) {
case JsonValue::Type::Null:
printf("%s", color_null);
break;
case JsonValue::Type::Bool:
printf("%s", color_bool);
break;
case JsonValue::Type::String:
printf("%s", color_string);
break;
default:
printf("%s", color_index);
break;
}
printf("%s%s;\n", value.serialized<StringBuilder>().characters(), color_off);
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCompress/Gzip.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/FileStream.h>
static void decompress_file(Buffered<Core::InputFileStream>& input_stream, Buffered<Core::OutputFileStream>& output_stream)
{
auto gzip_stream = Compress::GzipDecompressor { input_stream };
u8 buffer[4096];
while (!gzip_stream.unreliable_eof()) {
const auto nread = gzip_stream.read({ buffer, sizeof(buffer) });
output_stream.write_or_error({ buffer, nread });
}
}
int main(int argc, char** argv)
{
Vector<const char*> filenames;
bool keep_input_files { false };
bool write_to_stdout { false };
Core::ArgsParser args_parser;
args_parser.add_option(keep_input_files, "Keep (don't delete) input files", "keep", 'k');
args_parser.add_option(write_to_stdout, "Write to stdout, keep original files unchanged", "stdout", 'c');
args_parser.add_positional_argument(filenames, "File to decompress", "FILE");
args_parser.parse(argc, argv);
if (write_to_stdout)
keep_input_files = true;
for (String filename : filenames) {
if (!filename.ends_with(".gz"))
filename = String::format("%s.gz", filename.characters());
const auto input_filename = filename;
const auto output_filename = filename.substring_view(0, filename.length() - 3);
auto input_stream_result = Core::InputFileStream::open_buffered(input_filename);
if (write_to_stdout) {
auto stdout = Core::OutputFileStream::stdout_buffered();
decompress_file(input_stream_result.value(), stdout);
} else {
auto output_stream_result = Core::OutputFileStream::open_buffered(output_filename);
decompress_file(input_stream_result.value(), output_stream_result.value());
}
if (!keep_input_files) {
const auto retval = unlink(String { input_filename }.characters());
ASSERT(retval == 0);
}
}
}

162
Userland/Utilities/head.cpp Normal file
View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StdLibExtras.h>
#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int head(const String& filename, bool print_filename, int line_count, int char_count);
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
int line_count = 0;
int char_count = 0;
bool never_print_filenames = false;
bool always_print_filenames = false;
Vector<const char*> files;
Core::ArgsParser args_parser;
args_parser.set_general_help("Print the beginning ('head') of a file.");
args_parser.add_option(line_count, "Number of lines to print (default 10)", "lines", 'n', "number");
args_parser.add_option(char_count, "Number of characters to print", "characters", 'c', "number");
args_parser.add_option(never_print_filenames, "Never print file names", "quiet", 'q');
args_parser.add_option(always_print_filenames, "Always print file names", "verbose", 'v');
args_parser.add_positional_argument(files, "File to process", "file", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (line_count == 0 && char_count == 0) {
line_count = 10;
}
bool print_filenames = files.size() > 1;
if (always_print_filenames)
print_filenames = true;
else if (never_print_filenames)
print_filenames = false;
if (files.is_empty()) {
return head("", print_filenames, line_count, char_count);
}
int rc = 0;
for (auto& file : files) {
if (head(file, print_filenames, line_count, char_count) != 0) {
rc = 1;
}
}
return rc;
}
int head(const String& filename, bool print_filename, int line_count, int char_count)
{
bool is_stdin = false;
FILE* fp = nullptr;
if (filename == "" || filename == "-") {
fp = stdin;
is_stdin = true;
} else {
fp = fopen(filename.characters(), "r");
if (!fp) {
fprintf(stderr, "can't open %s for reading: %s\n", filename.characters(), strerror(errno));
return 1;
}
}
if (print_filename) {
if (is_stdin) {
puts("==> standard input <==");
} else {
printf("==> %s <==\n", filename.characters());
}
}
if (line_count) {
for (int line = 0; line < line_count; ++line) {
char buffer[BUFSIZ];
auto* str = fgets(buffer, sizeof(buffer), fp);
if (!str)
break;
// specifically use fputs rather than puts, because fputs doesn't add
// its own newline.
fputs(str, stdout);
}
} else if (char_count) {
char buffer[BUFSIZ];
while (char_count) {
int nread = fread(buffer, 1, min(BUFSIZ, char_count), fp);
if (nread > 0) {
int ncomplete = 0;
while (ncomplete < nread) {
int nwrote = fwrite(&buffer[ncomplete], 1, nread - ncomplete, stdout);
if (nwrote > 0)
ncomplete += nwrote;
if (feof(stdout)) {
fprintf(stderr, "unexpected eof writing to stdout\n");
return 1;
}
if (ferror(stdout)) {
fprintf(stderr, "error writing to stdout\n");
return 1;
}
}
}
char_count -= nread;
if (feof(fp))
break;
if (ferror(fp)) {
fprintf(stderr, "error reading input\n");
break;
}
}
}
fclose(fp);
if (print_filename) {
puts("");
}
return 0;
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/LogStream.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <ctype.h>
int main(int argc, char** argv)
{
Core::ArgsParser args_parser;
const char* path = nullptr;
args_parser.add_positional_argument(path, "Input", "input", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
RefPtr<Core::File> file;
if (!path) {
file = Core::File::standard_input();
} else {
auto file_or_error = Core::File::open(path, Core::File::ReadOnly);
if (file_or_error.is_error()) {
warnln("Failed to open {}: {}", path, file_or_error.error());
return 1;
}
file = file_or_error.value();
}
auto contents = file->read_all();
Vector<u8, 16> line;
auto print_line = [&] {
for (size_t i = 0; i < 16; ++i) {
if (i < line.size())
printf("%02x ", line[i]);
else
printf(" ");
if (i == 7)
printf(" ");
}
printf(" ");
for (size_t i = 0; i < 16; ++i) {
if (i < line.size() && isprint(line[i]))
putchar(line[i]);
else
putchar(' ');
}
putchar('\n');
};
for (size_t i = 0; i < contents.size(); ++i) {
line.append(contents[i]);
if (line.size() == 16) {
print_line();
line.clear();
}
}
if (!line.is_empty())
print_line();
return 0;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio dns", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* name_or_ip = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("Convert between domain name and IPv4 address.");
args_parser.add_positional_argument(name_or_ip, "Domain name or IPv4 address", "name");
args_parser.parse(argc, argv);
// If input looks like an IPv4 address, we should do a reverse lookup.
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(53);
int rc = inet_pton(AF_INET, name_or_ip, &addr.sin_addr);
if (rc == 1) {
// Okay, let's do a reverse lookup.
auto* hostent = gethostbyaddr(&addr.sin_addr, sizeof(in_addr), AF_INET);
if (!hostent) {
fprintf(stderr, "Reverse lookup failed for '%s'\n", name_or_ip);
return 1;
}
printf("%s is %s\n", name_or_ip, hostent->h_name);
return 0;
}
auto* hostent = gethostbyname(name_or_ip);
if (!hostent) {
fprintf(stderr, "Lookup failed for '%s'\n", name_or_ip);
return 1;
}
char buffer[32];
const char* ip_str = inet_ntop(AF_INET, hostent->h_addr_list[0], buffer, sizeof(buffer));
printf("%s is %s\n", name_or_ip, ip_str);
return 0;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
const char* hostname = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(hostname, "Hostname to set", "hostname", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (!hostname) {
char buffer[HOST_NAME_MAX];
int rc = gethostname(buffer, sizeof(buffer));
if (rc < 0) {
perror("gethostname");
return 1;
}
printf("%s\n", buffer);
} else {
if (strlen(hostname) >= HOST_NAME_MAX) {
fprintf(stderr, "Hostname must be less than %i characters\n", HOST_NAME_MAX);
return 1;
}
sethostname(hostname, strlen(hostname));
}
return 0;
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <LibCore/File.h>
#include <LibGUI/Action.h>
#include <LibGUI/Application.h>
#include <LibGUI/Icon.h>
#include <LibGUI/Menu.h>
#include <LibGUI/MenuBar.h>
#include <LibGUI/Window.h>
#include <LibGfx/Bitmap.h>
#include <LibWeb/OutOfProcessWebView.h>
#include <stdio.h>
int main(int argc, char** argv)
{
auto app = GUI::Application::construct(argc, argv);
auto f = Core::File::construct();
URL url;
bool success;
if (argc < 2) {
success = f->open(STDIN_FILENO, Core::IODevice::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::No);
} else {
url = URL::create_with_file_protocol(argv[1]);
f->set_filename(argv[1]);
success = f->open(Core::IODevice::OpenMode::ReadOnly);
}
if (!success) {
fprintf(stderr, "Error: %s\n", f->error_string());
return 1;
}
auto html = f->read_all();
auto window = GUI::Window::construct();
window->set_title("HTML");
auto& widget = window->set_main_widget<Web::OutOfProcessWebView>();
widget.on_title_change = [&](auto& title) {
window->set_title(String::formatted("{} - HTML", title));
};
widget.load_html(html, url);
window->show();
auto menubar = GUI::MenuBar::construct();
auto& app_menu = menubar->add_menu("HTML");
app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) {
app->quit();
}));
auto& help_menu = menubar->add_menu("Help");
help_menu.add_action(GUI::CommonActions::make_about_action("HTML", GUI::Icon::default_icon("filetype-html"), window));
app->set_menubar(move(menubar));
window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png"));
return app->exec();
}

180
Userland/Utilities/id.cpp Normal file
View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <alloca.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>
static int print_id_objects();
static bool flag_print_uid = false;
static bool flag_print_gid = false;
static bool flag_print_name = false;
static bool flag_print_gid_all = false;
int main(int argc, char** argv)
{
if (unveil("/etc/passwd", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/etc/group", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
Core::ArgsParser args_parser;
args_parser.add_option(flag_print_uid, "Print UID", nullptr, 'u');
args_parser.add_option(flag_print_gid, "Print GID", nullptr, 'g');
args_parser.add_option(flag_print_gid_all, "Print all GIDs", nullptr, 'G');
args_parser.add_option(flag_print_name, "Print name", nullptr, 'n');
args_parser.parse(argc, argv);
if (flag_print_name && !(flag_print_uid || flag_print_gid || flag_print_gid_all)) {
fprintf(stderr, "cannot print only names or real IDs in default format\n");
return 1;
}
if (flag_print_uid + flag_print_gid + flag_print_gid_all > 1) {
fprintf(stderr, "cannot print \"only\" of more than one choice\n");
return 1;
}
int status = print_id_objects();
return status;
}
static bool print_uid_object(uid_t uid)
{
if (flag_print_name) {
struct passwd* pw = getpwuid(uid);
printf("%s", pw ? pw->pw_name : "n/a");
} else
printf("%u", uid);
return true;
}
static bool print_gid_object(gid_t gid)
{
if (flag_print_name) {
struct group* gr = getgrgid(gid);
printf("%s", gr ? gr->gr_name : "n/a");
} else
printf("%u", gid);
return true;
}
static bool print_gid_list()
{
int extra_gid_count = getgroups(0, nullptr);
if (extra_gid_count) {
auto* extra_gids = (gid_t*)alloca(extra_gid_count * sizeof(gid_t));
int rc = getgroups(extra_gid_count, extra_gids);
if (rc < 0) {
perror("\ngetgroups");
return false;
}
for (int g = 0; g < extra_gid_count; ++g) {
auto* gr = getgrgid(extra_gids[g]);
if (flag_print_name && gr)
printf("%s", gr->gr_name);
else
printf("%u", extra_gids[g]);
if (g != extra_gid_count - 1)
printf(" ");
}
}
return true;
}
static bool print_full_id_list()
{
uid_t uid = getuid();
gid_t gid = getgid();
struct passwd* pw = getpwuid(uid);
struct group* gr = getgrgid(gid);
printf("uid=%u(%s) gid=%u(%s)", uid, pw ? pw->pw_name : "n/a", gid, gr ? gr->gr_name : "n/a");
int extra_gid_count = getgroups(0, nullptr);
if (extra_gid_count) {
auto* extra_gids = (gid_t*)alloca(extra_gid_count * sizeof(gid_t));
int rc = getgroups(extra_gid_count, extra_gids);
if (rc < 0) {
perror("\ngetgroups");
return false;
}
printf(" groups=");
for (int g = 0; g < extra_gid_count; ++g) {
auto* gr = getgrgid(extra_gids[g]);
if (gr)
printf("%u(%s)", extra_gids[g], gr->gr_name);
else
printf("%u", extra_gids[g]);
if (g != extra_gid_count - 1)
printf(",");
}
}
return true;
}
static int print_id_objects()
{
if (flag_print_uid) {
if (!print_uid_object(getuid()))
return 1;
} else if (flag_print_gid) {
if (!print_gid_object(getgid()))
return 1;
} else if (flag_print_gid_all) {
if (!print_gid_list())
return 1;
} else {
if (!print_full_id_list())
return 1;
}
printf("\n");
return 0;
}

View file

@ -0,0 +1,193 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/NumberFormat.h>
#include <AK/String.h>
#include <AK/Types.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
int main(int argc, char** argv)
{
const char* value_ipv4 = nullptr;
const char* value_adapter = nullptr;
const char* value_gateway = nullptr;
const char* value_mask = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("Display or modify the configuration of each network interface.");
args_parser.add_option(value_ipv4, "Set the IP address of the selected network", "ipv4", 'i', "The new IP of the network");
args_parser.add_option(value_adapter, "Select a specific network adapter to configure", "adapter", 'a', "The name of a network adapter");
args_parser.add_option(value_gateway, "Set the default gateway of the selected network", "gateway", 'g', "The new IP of the gateway");
args_parser.add_option(value_mask, "Set the network mask of the selected network", "mask", 'm', "The new network mask");
args_parser.parse(argc, argv);
if (!value_ipv4 && !value_adapter && !value_gateway && !value_mask) {
auto file = Core::File::construct("/proc/net/adapters");
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", file->error_string());
return 1;
}
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
json.value().as_array().for_each([](auto& value) {
auto if_object = value.as_object();
auto name = if_object.get("name").to_string();
auto class_name = if_object.get("class_name").to_string();
auto mac_address = if_object.get("mac_address").to_string();
auto ipv4_address = if_object.get("ipv4_address").to_string();
auto gateway = if_object.get("ipv4_gateway").to_string();
auto netmask = if_object.get("ipv4_netmask").to_string();
auto packets_in = if_object.get("packets_in").to_u32();
auto bytes_in = if_object.get("bytes_in").to_u32();
auto packets_out = if_object.get("packets_out").to_u32();
auto bytes_out = if_object.get("bytes_out").to_u32();
auto mtu = if_object.get("mtu").to_u32();
printf("%s:\n", name.characters());
printf("\tmac: %s\n", mac_address.characters());
printf("\tipv4: %s\n", ipv4_address.characters());
printf("\tnetmask: %s\n", netmask.characters());
printf("\tgateway: %s\n", gateway.characters());
printf("\tclass: %s\n", class_name.characters());
printf("\tRX: %u packets %u bytes (%s)\n", packets_in, bytes_in, human_readable_size(bytes_in).characters());
printf("\tTX: %u packets %u bytes (%s)\n", packets_out, bytes_out, human_readable_size(bytes_out).characters());
printf("\tMTU: %u\n", mtu);
printf("\n");
});
} else {
if (!value_adapter) {
fprintf(stderr, "No network adapter was specified.\n");
return 1;
}
String ifname = value_adapter;
if (value_ipv4) {
auto address = IPv4Address::from_string(value_ipv4);
if (!address.has_value()) {
fprintf(stderr, "Invalid IPv4 address: '%s'\n", value_ipv4);
return 1;
}
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
perror("socket");
return 1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
bool fits = ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
if (!fits) {
fprintf(stderr, "Interface name '%s' is too long\n", ifname.characters());
return 1;
}
ifr.ifr_addr.sa_family = AF_INET;
((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = address.value().to_in_addr_t();
int rc = ioctl(fd, SIOCSIFADDR, &ifr);
if (rc < 0) {
perror("ioctl(SIOCSIFADDR)");
return 1;
}
}
if (value_mask) {
auto address = IPv4Address::from_string(value_mask);
if (!address.has_value()) {
fprintf(stderr, "Invalid IPv4 mask: '%s'\n", value_mask);
return 1;
}
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
perror("socket");
return 1;
}
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
bool fits = ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
if (!fits) {
fprintf(stderr, "Interface name '%s' is too long\n", ifname.characters());
return 1;
}
ifr.ifr_netmask.sa_family = AF_INET;
((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = address.value().to_in_addr_t();
int rc = ioctl(fd, SIOCSIFNETMASK, &ifr);
if (rc < 0) {
perror("ioctl(SIOCSIFNETMASK)");
return 1;
}
}
if (value_gateway) {
auto address = IPv4Address::from_string(value_gateway);
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
perror("socket");
return 1;
}
struct rtentry rt;
memset(&rt, 0, sizeof(rt));
rt.rt_dev = const_cast<char*>(ifname.characters());
rt.rt_gateway.sa_family = AF_INET;
((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = address.value().to_in_addr_t();
rt.rt_flags = RTF_UP | RTF_GATEWAY;
int rc = ioctl(fd, SIOCADDRT, &rt);
if (rc < 0) {
perror("ioctl(SIOCADDRT)");
return 1;
}
}
}
return 0;
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/File.h>
#include <stdio.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath wpath cpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path = nullptr;
const char* group = nullptr;
const char* key = nullptr;
const char* value_to_write = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path to INI file", "path");
args_parser.add_positional_argument(group, "Group name", "group");
args_parser.add_positional_argument(key, "Key name", "key");
args_parser.add_positional_argument(value_to_write, "Value to write", "value", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (!Core::File::exists(path)) {
fprintf(stderr, "File does not exist: '%s'\n", path);
return 1;
}
auto config = Core::ConfigFile::open(path);
if (value_to_write) {
config->write_entry(group, key, value_to_write);
config->sync();
return 0;
}
auto value = config->read_entry(group, key);
if (!value.is_empty())
printf("%s\n", value.characters());
return 0;
}

137
Userland/Utilities/jp.cpp Normal file
View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <stdio.h>
#include <unistd.h>
static void print(const JsonValue& value, int indent = 0, bool use_color = true);
static void print_indent(int indent)
{
for (int i = 0; i < indent; ++i)
out(" ");
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("Pretty-print a JSON file with syntax-coloring and indentation.");
args_parser.add_positional_argument(path, "Path to JSON file", "path");
args_parser.parse(argc, argv);
auto file = Core::File::construct(path);
if (!file->open(Core::IODevice::ReadOnly)) {
warnln("Couldn't open {} for reading: {}", path, file->error_string());
return 1;
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
if (!json.has_value()) {
warnln("Couldn't parse {} as JSON", path);
return 1;
}
print(json.value(), 0, isatty(STDOUT_FILENO));
outln();
return 0;
}
void print(const JsonValue& value, int indent, bool use_color)
{
if (value.is_object()) {
size_t printed_members = 0;
auto object = value.as_object();
outln("{{");
object.for_each_member([&](auto& member_name, auto& member_value) {
++printed_members;
print_indent(indent + 1);
if (use_color)
out("\"\033[33;1m{}\033[0m\": ", member_name);
else
out("\"{}\": ", member_name);
print(member_value, indent + 1, use_color);
if (printed_members < static_cast<size_t>(object.size()))
out(",");
outln();
});
print_indent(indent);
out("}}");
return;
}
if (value.is_array()) {
size_t printed_entries = 0;
auto array = value.as_array();
outln("[");
array.for_each([&](auto& entry_value) {
++printed_entries;
print_indent(indent + 1);
print(entry_value, indent + 1, use_color);
if (printed_entries < static_cast<size_t>(array.size()))
out(",");
outln();
});
print_indent(indent);
out("]");
return;
}
if (use_color) {
if (value.is_string())
out("\033[31;1m");
else if (value.is_number())
out("\033[35;1m");
else if (value.is_bool())
out("\033[32;1m");
else if (value.is_null())
out("\033[34;1m");
}
if (value.is_string())
out("\"");
out("{}", value.to_string());
if (value.is_string())
out("\"");
if (use_color)
out("\033[0m");
}

918
Userland/Utilities/js.cpp Normal file
View file

@ -0,0 +1,918 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/Format.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/StandardPaths.h>
#include <LibJS/AST.h>
#include <LibJS/Console.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Parser.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/ArrayBuffer.h>
#include <LibJS/Runtime/BooleanObject.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/NativeFunction.h>
#include <LibJS/Runtime/NumberObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/PrimitiveString.h>
#include <LibJS/Runtime/ProxyObject.h>
#include <LibJS/Runtime/RegExpObject.h>
#include <LibJS/Runtime/ScriptFunction.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/Value.h>
#include <LibLine/Editor.h>
#include <signal.h>
#include <stdio.h>
RefPtr<JS::VM> vm;
Vector<String> repl_statements;
class ReplObject : public JS::GlobalObject {
public:
ReplObject();
virtual void initialize() override;
virtual ~ReplObject() override;
private:
virtual const char* class_name() const override { return "ReplObject"; }
JS_DECLARE_NATIVE_FUNCTION(exit_interpreter);
JS_DECLARE_NATIVE_FUNCTION(repl_help);
JS_DECLARE_NATIVE_FUNCTION(load_file);
JS_DECLARE_NATIVE_FUNCTION(save_to_file);
};
static bool s_dump_ast = false;
static bool s_print_last_result = false;
static RefPtr<Line::Editor> s_editor;
static String s_history_path = String::formatted("{}/.js-history", Core::StandardPaths::home_directory());
static int s_repl_line_level = 0;
static bool s_fail_repl = false;
static String prompt_for_level(int level)
{
static StringBuilder prompt_builder;
prompt_builder.clear();
prompt_builder.append("> ");
for (auto i = 0; i < level; ++i)
prompt_builder.append(" ");
return prompt_builder.build();
}
static String read_next_piece()
{
StringBuilder piece;
auto line_level_delta_for_next_line { 0 };
do {
auto line_result = s_editor->get_line(prompt_for_level(s_repl_line_level));
line_level_delta_for_next_line = 0;
if (line_result.is_error()) {
s_fail_repl = true;
return "";
}
auto& line = line_result.value();
s_editor->add_to_history(line);
piece.append(line);
auto lexer = JS::Lexer(line);
enum {
NotInLabelOrObjectKey,
InLabelOrObjectKeyIdentifier,
InLabelOrObjectKey
} label_state { NotInLabelOrObjectKey };
for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) {
switch (token.type()) {
case JS::TokenType::BracketOpen:
case JS::TokenType::CurlyOpen:
case JS::TokenType::ParenOpen:
label_state = NotInLabelOrObjectKey;
s_repl_line_level++;
break;
case JS::TokenType::BracketClose:
case JS::TokenType::CurlyClose:
case JS::TokenType::ParenClose:
label_state = NotInLabelOrObjectKey;
s_repl_line_level--;
break;
case JS::TokenType::Identifier:
case JS::TokenType::StringLiteral:
if (label_state == NotInLabelOrObjectKey)
label_state = InLabelOrObjectKeyIdentifier;
else
label_state = NotInLabelOrObjectKey;
break;
case JS::TokenType::Colon:
if (label_state == InLabelOrObjectKeyIdentifier)
label_state = InLabelOrObjectKey;
else
label_state = NotInLabelOrObjectKey;
break;
default:
break;
}
}
if (label_state == InLabelOrObjectKey) {
// If there's a label or object literal key at the end of this line,
// prompt for more lines but do not change the line level.
line_level_delta_for_next_line += 1;
}
} while (s_repl_line_level + line_level_delta_for_next_line > 0);
return piece.to_string();
}
static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects);
static void print_type(const FlyString& name)
{
out("[\033[36;1m{}\033[0m]", name);
}
static void print_separator(bool& first)
{
out(first ? " " : ", ");
first = false;
}
static void print_array(JS::Array& array, HashTable<JS::Object*>& seen_objects)
{
out("[");
bool first = true;
for (auto it = array.indexed_properties().begin(false); it != array.indexed_properties().end(); ++it) {
print_separator(first);
auto value = it.value_and_attributes(&array).value;
// The V8 repl doesn't throw an exception here, and instead just
// prints 'undefined'. We may choose to replicate that behavior in
// the future, but for now lets just catch the error
if (vm->exception())
return;
print_value(value, seen_objects);
}
if (!first)
out(" ");
out("]");
}
static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_objects)
{
out("{{");
bool first = true;
for (auto& entry : object.indexed_properties()) {
print_separator(first);
out("\"\033[33;1m{}\033[0m\": ", entry.index());
auto value = entry.value_and_attributes(&object).value;
// The V8 repl doesn't throw an exception here, and instead just
// prints 'undefined'. We may choose to replicate that behavior in
// the future, but for now lets just catch the error
if (vm->exception())
return;
print_value(value, seen_objects);
}
for (auto& it : object.shape().property_table_ordered()) {
print_separator(first);
if (it.key.is_string()) {
out("\"\033[33;1m{}\033[0m\": ", it.key.to_display_string());
} else {
out("[\033[33;1m{}\033[0m]: ", it.key.to_display_string());
}
print_value(object.get_direct(it.value.offset), seen_objects);
}
if (!first)
out(" ");
out("}}");
}
static void print_function(const JS::Object& object, HashTable<JS::Object*>&)
{
print_type(object.class_name());
if (is<JS::ScriptFunction>(object))
out(" {}", static_cast<const JS::ScriptFunction&>(object).name());
else if (is<JS::NativeFunction>(object))
out(" {}", static_cast<const JS::NativeFunction&>(object).name());
}
static void print_date(const JS::Object& date, HashTable<JS::Object*>&)
{
print_type("Date");
out(" \033[34;1m{}\033[0m", static_cast<const JS::Date&>(date).string());
}
static void print_error(const JS::Object& object, HashTable<JS::Object*>&)
{
auto& error = static_cast<const JS::Error&>(object);
print_type(error.name());
if (!error.message().is_empty())
out(" \033[31;1m{}\033[0m", error.message());
}
static void print_regexp_object(const JS::Object& object, HashTable<JS::Object*>&)
{
auto& regexp_object = static_cast<const JS::RegExpObject&>(object);
// Use RegExp.prototype.source rather than RegExpObject::pattern() so we get proper escaping
auto source = regexp_object.get("source").to_primitive_string(object.global_object())->string();
print_type("RegExp");
out(" \033[34;1m/{}/{}\033[0m", source, regexp_object.flags());
}
static void print_proxy_object(const JS::Object& object, HashTable<JS::Object*>& seen_objects)
{
auto& proxy_object = static_cast<const JS::ProxyObject&>(object);
print_type("Proxy");
out("\n target: ");
print_value(&proxy_object.target(), seen_objects);
out("\n handler: ");
print_value(&proxy_object.handler(), seen_objects);
}
static void print_array_buffer(const JS::Object& object, HashTable<JS::Object*>& seen_objects)
{
auto& array_buffer = static_cast<const JS::ArrayBuffer&>(object);
auto& buffer = array_buffer.buffer();
auto byte_length = array_buffer.byte_length();
print_type("ArrayBuffer");
out("\n byteLength: ");
print_value(JS::Value((double)byte_length), seen_objects);
outln();
for (size_t i = 0; i < byte_length; ++i) {
out("{:02x}", buffer[i]);
if (i + 1 < byte_length) {
if ((i + 1) % 32 == 0)
outln();
else if ((i + 1) % 16 == 0)
out(" ");
else
out(" ");
}
}
}
static void print_typed_array(const JS::Object& object, HashTable<JS::Object*>& seen_objects)
{
auto& typed_array_base = static_cast<const JS::TypedArrayBase&>(object);
auto length = typed_array_base.array_length();
print_type(object.class_name());
out("\n length: ");
print_value(JS::Value(length), seen_objects);
out("\n byteLength: ");
print_value(JS::Value(typed_array_base.byte_length()), seen_objects);
out("\n buffer: ");
print_type("ArrayBuffer");
out(" @ {:p}", typed_array_base.viewed_array_buffer());
if (!length)
return;
outln();
// FIXME: This kinda sucks.
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (StringView(object.class_name()) == StringView(#ClassName)) { \
out("[ "); \
auto& typed_array = static_cast<const JS::ClassName&>(typed_array_base); \
auto data = typed_array.data(); \
for (size_t i = 0; i < length; ++i) { \
if (i > 0) \
out(", "); \
print_value(JS::Value(data[i]), seen_objects); \
} \
out(" ]"); \
return; \
}
JS_ENUMERATE_TYPED_ARRAYS
#undef __JS_ENUMERATE
ASSERT_NOT_REACHED();
}
static void print_primitive_wrapper_object(const FlyString& name, const JS::Object& object, HashTable<JS::Object*>& seen_objects)
{
// BooleanObject, NumberObject, StringObject
print_type(name);
out(" ");
print_value(object.value_of(), seen_objects);
}
static void print_value(JS::Value value, HashTable<JS::Object*>& seen_objects)
{
if (value.is_empty()) {
out("\033[34;1m<empty>\033[0m");
return;
}
if (value.is_object()) {
if (seen_objects.contains(&value.as_object())) {
// FIXME: Maybe we should only do this for circular references,
// not for all reoccurring objects.
out("<already printed Object {}>", &value.as_object());
return;
}
seen_objects.set(&value.as_object());
}
if (value.is_array())
return print_array(static_cast<JS::Array&>(value.as_object()), seen_objects);
if (value.is_object()) {
auto& object = value.as_object();
if (object.is_function())
return print_function(object, seen_objects);
if (is<JS::Date>(object))
return print_date(object, seen_objects);
if (is<JS::Error>(object))
return print_error(object, seen_objects);
if (is<JS::RegExpObject>(object))
return print_regexp_object(object, seen_objects);
if (is<JS::ProxyObject>(object))
return print_proxy_object(object, seen_objects);
if (is<JS::ArrayBuffer>(object))
return print_array_buffer(object, seen_objects);
if (object.is_typed_array())
return print_typed_array(object, seen_objects);
if (is<JS::StringObject>(object))
return print_primitive_wrapper_object("String", object, seen_objects);
if (is<JS::NumberObject>(object))
return print_primitive_wrapper_object("Number", object, seen_objects);
if (is<JS::BooleanObject>(object))
return print_primitive_wrapper_object("Boolean", object, seen_objects);
return print_object(object, seen_objects);
}
if (value.is_string())
out("\033[32;1m");
else if (value.is_number() || value.is_bigint())
out("\033[35;1m");
else if (value.is_boolean())
out("\033[33;1m");
else if (value.is_null())
out("\033[33;1m");
else if (value.is_undefined())
out("\033[34;1m");
if (value.is_string())
out("\"");
else if (value.is_negative_zero())
out("-");
out("{}", value.to_string_without_side_effects());
if (value.is_string())
out("\"");
out("\033[0m");
}
static void print(JS::Value value)
{
HashTable<JS::Object*> seen_objects;
print_value(value, seen_objects);
outln();
}
static bool file_has_shebang(AK::ByteBuffer file_contents)
{
if (file_contents.size() >= 2 && file_contents[0] == '#' && file_contents[1] == '!')
return true;
return false;
}
static StringView strip_shebang(AK::ByteBuffer file_contents)
{
size_t i = 0;
for (i = 2; i < file_contents.size(); ++i) {
if (file_contents[i] == '\n')
break;
}
return StringView((const char*)file_contents.data() + i, file_contents.size() - i);
}
static bool write_to_file(const StringView& path)
{
int fd = open_with_path_length(path.characters_without_null_termination(), path.length(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
for (size_t i = 0; i < repl_statements.size(); i++) {
auto line = repl_statements[i];
if (line.length() && i != repl_statements.size() - 1) {
ssize_t nwritten = write(fd, line.characters(), line.length());
if (nwritten < 0) {
close(fd);
return false;
}
}
if (i != repl_statements.size() - 1) {
char ch = '\n';
ssize_t nwritten = write(fd, &ch, 1);
if (nwritten != 1) {
perror("write");
close(fd);
return false;
}
}
}
close(fd);
return true;
}
static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source)
{
auto parser = JS::Parser(JS::Lexer(source));
auto program = parser.parse_program();
if (s_dump_ast)
program->dump(0);
if (parser.has_errors()) {
auto error = parser.errors()[0];
auto hint = error.source_location_hint(source);
if (!hint.is_empty())
outln("{}", hint);
vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string());
} else {
interpreter.run(interpreter.global_object(), *program);
}
if (vm->exception()) {
out("Uncaught exception: ");
print(vm->exception()->value());
auto trace = vm->exception()->trace();
if (trace.size() > 1) {
unsigned repetitions = 0;
for (size_t i = 0; i < trace.size(); ++i) {
auto& function_name = trace[i];
if (i + 1 < trace.size() && trace[i + 1] == function_name) {
repetitions++;
continue;
}
if (repetitions > 4) {
// If more than 5 (1 + >4) consecutive function calls with the same name, print
// the name only once and show the number of repetitions instead. This prevents
// printing ridiculously large call stacks of recursive functions.
outln(" -> {}", function_name);
outln(" {} more calls", repetitions);
} else {
for (size_t j = 0; j < repetitions + 1; ++j)
outln(" -> {}", function_name);
}
repetitions = 0;
}
}
vm->clear_exception();
return false;
}
if (s_print_last_result)
print(vm->last_value());
return true;
}
ReplObject::ReplObject()
{
}
void ReplObject::initialize()
{
GlobalObject::initialize();
define_property("global", this, JS::Attribute::Enumerable);
define_native_function("exit", exit_interpreter);
define_native_function("help", repl_help);
define_native_function("load", load_file, 1);
define_native_function("save", save_to_file, 1);
}
ReplObject::~ReplObject()
{
}
JS_DEFINE_NATIVE_FUNCTION(ReplObject::save_to_file)
{
if (!vm.argument_count())
return JS::Value(false);
String save_path = vm.argument(0).to_string_without_side_effects();
StringView path = StringView(save_path.characters());
if (write_to_file(path)) {
return JS::Value(true);
}
return JS::Value(false);
}
JS_DEFINE_NATIVE_FUNCTION(ReplObject::exit_interpreter)
{
if (!vm.argument_count())
exit(0);
auto exit_code = vm.argument(0).to_number(global_object);
if (::vm->exception())
return {};
exit(exit_code.as_double());
}
JS_DEFINE_NATIVE_FUNCTION(ReplObject::repl_help)
{
outln("REPL commands:");
outln(" exit(code): exit the REPL with specified code. Defaults to 0.");
outln(" help(): display this menu");
outln(" load(files): accepts file names as params to load into running session. For example load(\"js/1.js\", \"js/2.js\", \"js/3.js\")");
outln(" save(file): accepts a file name, writes REPL input history to a file. For example: save(\"foo.txt\")");
return JS::js_undefined();
}
JS_DEFINE_NATIVE_FUNCTION(ReplObject::load_file)
{
if (!vm.argument_count())
return JS::Value(false);
for (auto& file : vm.call_frame().arguments) {
String file_name = file.as_string().string();
auto js_file = Core::File::construct(file_name);
if (!js_file->open(Core::IODevice::ReadOnly)) {
warnln("Failed to open {}: {}", file_name, js_file->error_string());
continue;
}
auto file_contents = js_file->read_all();
StringView source;
if (file_has_shebang(file_contents)) {
source = strip_shebang(file_contents);
} else {
source = file_contents;
}
parse_and_run(vm.interpreter(), source);
}
return JS::Value(true);
}
static void repl(JS::Interpreter& interpreter)
{
while (!s_fail_repl) {
String piece = read_next_piece();
if (piece.is_empty())
continue;
repl_statements.append(piece);
parse_and_run(interpreter, piece);
}
}
static Function<void()> interrupt_interpreter;
static void sigint_handler()
{
interrupt_interpreter();
}
class ReplConsoleClient final : public JS::ConsoleClient {
public:
ReplConsoleClient(JS::Console& console)
: ConsoleClient(console)
{
}
virtual JS::Value log() override
{
outln("{}", vm().join_arguments());
return JS::js_undefined();
}
virtual JS::Value info() override
{
outln("(i) {}", vm().join_arguments());
return JS::js_undefined();
}
virtual JS::Value debug() override
{
outln("\033[36;1m{}\033[0m", vm().join_arguments());
return JS::js_undefined();
}
virtual JS::Value warn() override
{
outln("\033[33;1m{}\033[0m", vm().join_arguments());
return JS::js_undefined();
}
virtual JS::Value error() override
{
outln("\033[31;1m{}\033[0m", vm().join_arguments());
return JS::js_undefined();
}
virtual JS::Value clear() override
{
out("\033[3J\033[H\033[2J");
fflush(stdout);
return JS::js_undefined();
}
virtual JS::Value trace() override
{
outln("{}", vm().join_arguments());
auto trace = get_trace();
for (auto& function_name : trace) {
if (function_name.is_empty())
function_name = "<anonymous>";
outln(" -> {}", function_name);
}
return JS::js_undefined();
}
virtual JS::Value count() override
{
auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default";
auto counter_value = m_console.counter_increment(label);
outln("{}: {}", label, counter_value);
return JS::js_undefined();
}
virtual JS::Value count_reset() override
{
auto label = vm().argument_count() ? vm().argument(0).to_string_without_side_effects() : "default";
if (m_console.counter_reset(label))
outln("{}: 0", label);
else
outln("\033[33;1m\"{}\" doesn't have a count\033[0m", label);
return JS::js_undefined();
}
};
int main(int argc, char** argv)
{
bool gc_on_every_allocation = false;
bool disable_syntax_highlight = false;
const char* script_path = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("This is a JavaScript interpreter.");
args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A');
args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l');
args_parser.add_option(gc_on_every_allocation, "GC on every allocation", "gc-on-every-allocation", 'g');
args_parser.add_option(disable_syntax_highlight, "Disable live syntax highlighting", "no-syntax-highlight", 's');
args_parser.add_positional_argument(script_path, "Path to script file", "script", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
bool syntax_highlight = !disable_syntax_highlight;
vm = JS::VM::create();
OwnPtr<JS::Interpreter> interpreter;
interrupt_interpreter = [&] {
auto error = JS::Error::create(interpreter->global_object(), "Error", "Received SIGINT");
vm->throw_exception(interpreter->global_object(), error);
};
if (script_path == nullptr) {
s_print_last_result = true;
interpreter = JS::Interpreter::create<ReplObject>(*vm);
ReplConsoleClient console_client(interpreter->global_object().console());
interpreter->global_object().console().set_client(console_client);
interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation);
interpreter->vm().set_underscore_is_last_value(true);
s_editor = Line::Editor::construct();
s_editor->load_history(s_history_path);
signal(SIGINT, [](int) {
if (!s_editor->is_editing())
sigint_handler();
s_editor->save_history(s_history_path);
});
s_editor->on_display_refresh = [syntax_highlight](Line::Editor& editor) {
auto stylize = [&](Line::Span span, Line::Style styles) {
if (syntax_highlight)
editor.stylize(span, styles);
};
editor.strip_styles();
size_t open_indents = s_repl_line_level;
auto line = editor.line();
JS::Lexer lexer(line);
bool indenters_starting_line = true;
for (JS::Token token = lexer.next(); token.type() != JS::TokenType::Eof; token = lexer.next()) {
auto length = token.value().length();
auto start = token.line_column() - 1;
auto end = start + length;
if (indenters_starting_line) {
if (token.type() != JS::TokenType::ParenClose && token.type() != JS::TokenType::BracketClose && token.type() != JS::TokenType::CurlyClose) {
indenters_starting_line = false;
} else {
--open_indents;
}
}
switch (token.category()) {
case JS::TokenCategory::Invalid:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Red), Line::Style::Underline });
break;
case JS::TokenCategory::Number:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Magenta) });
break;
case JS::TokenCategory::String:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Green), Line::Style::Bold });
break;
case JS::TokenCategory::Punctuation:
break;
case JS::TokenCategory::Operator:
break;
case JS::TokenCategory::Keyword:
switch (token.type()) {
case JS::TokenType::BoolLiteral:
case JS::TokenType::NullLiteral:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Yellow), Line::Style::Bold });
break;
default:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Blue), Line::Style::Bold });
break;
}
break;
case JS::TokenCategory::ControlKeyword:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::Cyan), Line::Style::Italic });
break;
case JS::TokenCategory::Identifier:
stylize({ start, end }, { Line::Style::Foreground(Line::Style::XtermColor::White), Line::Style::Bold });
default:
break;
}
}
editor.set_prompt(prompt_for_level(open_indents));
};
auto complete = [&interpreter](const Line::Editor& editor) -> Vector<Line::CompletionSuggestion> {
auto line = editor.line(editor.cursor());
JS::Lexer lexer { line };
enum {
Initial,
CompleteVariable,
CompleteNullProperty,
CompleteProperty,
} mode { Initial };
StringView variable_name;
StringView property_name;
// we're only going to complete either
// - <N>
// where N is part of the name of a variable
// - <N>.<P>
// where N is the complete name of a variable and
// P is part of the name of one of its properties
auto js_token = lexer.next();
for (; js_token.type() != JS::TokenType::Eof; js_token = lexer.next()) {
switch (mode) {
case CompleteVariable:
switch (js_token.type()) {
case JS::TokenType::Period:
// ...<name> <dot>
mode = CompleteNullProperty;
break;
default:
// not a dot, reset back to initial
mode = Initial;
break;
}
break;
case CompleteNullProperty:
if (js_token.is_identifier_name()) {
// ...<name> <dot> <name>
mode = CompleteProperty;
property_name = js_token.value();
} else {
mode = Initial;
}
break;
case CompleteProperty:
// something came after the property access, reset to initial
case Initial:
if (js_token.is_identifier_name()) {
// ...<name>...
mode = CompleteVariable;
variable_name = js_token.value();
} else {
mode = Initial;
}
break;
}
}
bool last_token_has_trivia = js_token.trivia().length() > 0;
if (mode == CompleteNullProperty) {
mode = CompleteProperty;
property_name = "";
last_token_has_trivia = false; // <name> <dot> [tab] is sensible to complete.
}
if (mode == Initial || last_token_has_trivia)
return {}; // we do not know how to complete this
Vector<Line::CompletionSuggestion> results;
Function<void(const JS::Shape&, const StringView&)> list_all_properties = [&results, &list_all_properties](const JS::Shape& shape, auto& property_pattern) {
for (const auto& descriptor : shape.property_table()) {
if (!descriptor.key.is_string())
continue;
auto key = descriptor.key.as_string();
if (key.view().starts_with(property_pattern)) {
Line::CompletionSuggestion completion { key, Line::CompletionSuggestion::ForSearch };
if (!results.contains_slow(completion)) { // hide duplicates
results.append(key);
}
}
}
if (const auto* prototype = shape.prototype()) {
list_all_properties(prototype->shape(), property_pattern);
}
};
switch (mode) {
case CompleteProperty: {
auto maybe_variable = vm->get_variable(variable_name, interpreter->global_object());
if (maybe_variable.is_empty()) {
maybe_variable = interpreter->global_object().get(FlyString(variable_name));
if (maybe_variable.is_empty())
break;
}
auto variable = maybe_variable;
if (!variable.is_object())
break;
const auto* object = variable.to_object(interpreter->global_object());
const auto& shape = object->shape();
list_all_properties(shape, property_name);
if (results.size())
editor.suggest(property_name.length());
break;
}
case CompleteVariable: {
const auto& variable = interpreter->global_object();
list_all_properties(variable.shape(), variable_name);
if (results.size())
editor.suggest(variable_name.length());
break;
}
default:
ASSERT_NOT_REACHED();
}
return results;
};
s_editor->on_tab_complete = move(complete);
repl(*interpreter);
s_editor->save_history(s_history_path);
} else {
interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm);
ReplConsoleClient console_client(interpreter->global_object().console());
interpreter->global_object().console().set_client(console_client);
interpreter->heap().set_should_collect_on_every_allocation(gc_on_every_allocation);
signal(SIGINT, [](int) {
sigint_handler();
});
auto file = Core::File::construct(script_path);
if (!file->open(Core::IODevice::ReadOnly)) {
warnln("Failed to open {}: {}", script_path, file->error_string());
return 1;
}
auto file_contents = file->read_all();
StringView source;
if (file_has_shebang(file_contents)) {
source = strip_shebang(file_contents);
} else {
source = file_contents;
}
if (!parse_and_run(*interpreter, source))
return 1;
}
return 0;
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibKeyboard/CharacterMap.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio setkeymap rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/res/keymaps", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "The mapping file to be used", "file");
args_parser.parse(argc, argv);
Keyboard::CharacterMap character_map(path);
int rc = character_map.set_system_map();
if (rc != 0)
fprintf(stderr, "%s\n", strerror(-rc));
return rc;
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Optional.h>
#include <AK/String.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static void print_usage_and_exit()
{
printf("usage: kill [-signal] <PID>\n");
exit(1);
}
int main(int argc, char** argv)
{
if (pledge("stdio proc", nullptr) < 0) {
perror("pledge");
return 1;
}
if (argc == 2 && !strcmp(argv[1], "-l")) {
for (size_t i = 0; i < NSIG; ++i) {
if (i && !(i % 5))
outln("");
out("{:2}) {:10}", i, getsignalname(i));
}
outln("");
return 0;
}
if (argc != 2 && argc != 3)
print_usage_and_exit();
unsigned signum = SIGTERM;
int pid_argi = 1;
if (argc == 3) {
pid_argi = 2;
if (argv[1][0] != '-')
print_usage_and_exit();
Optional<unsigned> number;
if (isalpha(argv[1][1])) {
int value = getsignalbyname(&argv[1][1]);
if (value >= 0 && value < NSIG)
number = value;
}
if (!number.has_value())
number = StringView(&argv[1][1]).to_uint();
if (!number.has_value()) {
printf("'%s' is not a valid signal name or number\n", &argv[1][1]);
return 2;
}
signum = number.value();
}
auto pid_opt = String(argv[pid_argi]).to_int();
if (!pid_opt.has_value()) {
printf("'%s' is not a valid PID\n", argv[pid_argi]);
return 3;
}
pid_t pid = pid_opt.value();
int rc = kill(pid, signum);
if (rc < 0)
perror("kill");
return 0;
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <LibCore/ProcessStatisticsReader.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void print_usage_and_exit()
{
printf("usage: killall [-signal] process_name\n");
exit(1);
}
static int kill_all(const String& process_name, const unsigned signum)
{
auto processes = Core::ProcessStatisticsReader().get_all();
if (!processes.has_value())
return 1;
for (auto& it : processes.value()) {
if (it.value.name == process_name) {
int ret = kill(it.value.pid, signum);
if (ret < 0)
perror("kill");
}
}
return 0;
}
int main(int argc, char** argv)
{
unsigned signum = SIGTERM;
int name_argi = 1;
if (argc != 2 && argc != 3)
print_usage_and_exit();
if (argc == 3) {
name_argi = 2;
if (argv[1][0] != '-')
print_usage_and_exit();
Optional<unsigned> number;
if (isalpha(argv[1][1])) {
int value = getsignalbyname(&argv[1][1]);
if (value >= 0 && value < NSIG)
number = value;
}
if (!number.has_value())
number = String(&argv[1][1]).to_uint();
if (!number.has_value()) {
printf("'%s' is not a valid signal name or number\n", &argv[1][1]);
return 2;
}
signum = number.value();
}
return kill_all(argv[name_argi], signum);
}

72
Userland/Utilities/ln.cpp Normal file
View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio cpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool symbolic = false;
const char* target = nullptr;
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(symbolic, "Create a symlink", "symbolic", 's');
args_parser.add_positional_argument(target, "Link target", "target");
args_parser.add_positional_argument(path, "Link path", "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
String path_buffer;
if (!path) {
path_buffer = LexicalPath(target).basename();
path = path_buffer.characters();
}
if (symbolic) {
int rc = symlink(target, path);
if (rc < 0) {
perror("symlink");
return 1;
}
return 0;
}
int rc = link(target, path);
if (rc < 0) {
perror("link");
return 1;
}
return 0;
}

504
Userland/Utilities/ls.cpp Normal file
View file

@ -0,0 +1,504 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/HashMap.h>
#include <AK/NumberFormat.h>
#include <AK/QuickSort.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/Utf8View.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
static int do_file_system_object_long(const char* path);
static int do_file_system_object_short(const char* path);
static bool flag_classify = false;
static bool flag_colorize = false;
static bool flag_long = false;
static bool flag_show_dotfiles = false;
static bool flag_show_almost_all_dotfiles = false;
static bool flag_ignore_backups = false;
static bool flag_list_directories_only = false;
static bool flag_show_inode = false;
static bool flag_print_numeric = false;
static bool flag_hide_group = false;
static bool flag_human_readable = false;
static bool flag_sort_by_timestamp = false;
static bool flag_reverse_sort = false;
static bool flag_disable_hyperlinks = false;
static size_t terminal_rows = 0;
static size_t terminal_columns = 0;
static bool output_is_terminal = false;
static HashMap<uid_t, String> users;
static HashMap<gid_t, String> groups;
int main(int argc, char** argv)
{
if (pledge("stdio rpath tty", nullptr) < 0) {
perror("pledge");
return 1;
}
struct winsize ws;
int rc = ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
if (rc == 0) {
terminal_rows = ws.ws_row;
terminal_columns = ws.ws_col;
output_is_terminal = true;
}
if (!isatty(STDOUT_FILENO)) {
flag_disable_hyperlinks = true;
} else {
flag_colorize = true;
}
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.set_general_help("List files in a directory.");
args_parser.add_option(flag_show_dotfiles, "Show dotfiles", "all", 'a');
args_parser.add_option(flag_show_almost_all_dotfiles, "Do not list implied . and .. directories", nullptr, 'A');
args_parser.add_option(flag_ignore_backups, "Do not list implied entries ending with ~", "--ignore-backups", 'B');
args_parser.add_option(flag_list_directories_only, "List directories themselves, not their contents", "directory", 'd');
args_parser.add_option(flag_long, "Display long info", "long", 'l');
args_parser.add_option(flag_sort_by_timestamp, "Sort files by timestamp", nullptr, 't');
args_parser.add_option(flag_reverse_sort, "Reverse sort order", "reverse", 'r');
args_parser.add_option(flag_classify, "Append a file type indicator to entries", "classify", 'F');
args_parser.add_option(flag_colorize, "Use pretty colors", nullptr, 'G');
args_parser.add_option(flag_show_inode, "Show inode ids", "inode", 'i');
args_parser.add_option(flag_print_numeric, "In long format, display numeric UID/GID", "numeric-uid-gid", 'n');
args_parser.add_option(flag_hide_group, "In long format, do not show group information", nullptr, 'o');
args_parser.add_option(flag_human_readable, "Print human-readable sizes", "human-readable", 'h');
args_parser.add_option(flag_disable_hyperlinks, "Disable hyperlinks", "no-hyperlinks", 'K');
args_parser.add_positional_argument(paths, "Directory to list", "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (flag_show_almost_all_dotfiles)
flag_show_dotfiles = true;
if (flag_long) {
setpwent();
for (auto* pwd = getpwent(); pwd; pwd = getpwent())
users.set(pwd->pw_uid, pwd->pw_name);
endpwent();
setgrent();
for (auto* grp = getgrent(); grp; grp = getgrent())
groups.set(grp->gr_gid, grp->gr_name);
endgrent();
}
auto do_file_system_object = [&](const char* path) {
if (flag_long)
return do_file_system_object_long(path);
return do_file_system_object_short(path);
};
int status = 0;
if (paths.is_empty()) {
status = do_file_system_object(".");
} else if (paths.size() == 1) {
status = do_file_system_object(paths[0]);
} else {
for (auto& path : paths) {
status = do_file_system_object(path);
}
}
return status;
}
static int print_escaped(const char* name)
{
int printed = 0;
Utf8View utf8_name(name);
if (utf8_name.validate()) {
printf("%s", name);
return utf8_name.length();
}
for (int i = 0; name[i] != '\0'; i++) {
if (isprint(name[i])) {
putchar(name[i]);
printed++;
} else {
printed += printf("\\%03d", name[i]);
}
}
return printed;
}
static String& hostname()
{
static String s_hostname;
if (s_hostname.is_null()) {
char buffer[HOST_NAME_MAX];
if (gethostname(buffer, sizeof(buffer)) == 0)
s_hostname = buffer;
else
s_hostname = "localhost";
}
return s_hostname;
}
static size_t print_name(const struct stat& st, const String& name, const char* path_for_link_resolution, const char* path_for_hyperlink)
{
if (!flag_disable_hyperlinks) {
auto full_path = Core::File::real_path_for(path_for_hyperlink);
if (!full_path.is_null()) {
out("\033]8;;file://{}{}\033\\", hostname(), full_path);
}
}
size_t nprinted = 0;
if (!flag_colorize || !output_is_terminal) {
nprinted = printf("%s", name.characters());
} else {
const char* begin_color = "";
const char* end_color = "\033[0m";
if (st.st_mode & S_ISVTX)
begin_color = "\033[42;30;1m";
else if (st.st_mode & S_ISUID)
begin_color = "\033[41;1m";
else if (st.st_mode & S_ISGID)
begin_color = "\033[43;1m";
else if (S_ISLNK(st.st_mode))
begin_color = "\033[36;1m";
else if (S_ISDIR(st.st_mode))
begin_color = "\033[34;1m";
else if (st.st_mode & 0111)
begin_color = "\033[32;1m";
else if (S_ISSOCK(st.st_mode))
begin_color = "\033[35;1m";
else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
begin_color = "\033[33;1m";
printf("%s", begin_color);
nprinted = print_escaped(name.characters());
printf("%s", end_color);
}
if (S_ISLNK(st.st_mode)) {
if (path_for_link_resolution) {
auto link_destination = Core::File::read_link(path_for_link_resolution);
if (link_destination.is_null()) {
perror("readlink");
} else {
nprinted += printf(" -> ") + print_escaped(link_destination.characters());
}
} else {
if (flag_classify)
nprinted += printf("@");
}
} else if (S_ISDIR(st.st_mode)) {
if (flag_classify)
nprinted += printf("/");
} else if (st.st_mode & 0111) {
if (flag_classify)
nprinted += printf("*");
}
if (!flag_disable_hyperlinks) {
printf("\033]8;;\033\\");
}
return nprinted;
}
static bool print_filesystem_object(const String& path, const String& name, const struct stat& st)
{
if (flag_show_inode)
printf("%08u ", st.st_ino);
if (S_ISDIR(st.st_mode))
printf("d");
else if (S_ISLNK(st.st_mode))
printf("l");
else if (S_ISBLK(st.st_mode))
printf("b");
else if (S_ISCHR(st.st_mode))
printf("c");
else if (S_ISFIFO(st.st_mode))
printf("f");
else if (S_ISSOCK(st.st_mode))
printf("s");
else if (S_ISREG(st.st_mode))
printf("-");
else
printf("?");
printf("%c%c%c%c%c%c%c%c",
st.st_mode & S_IRUSR ? 'r' : '-',
st.st_mode & S_IWUSR ? 'w' : '-',
st.st_mode & S_ISUID ? 's' : (st.st_mode & S_IXUSR ? 'x' : '-'),
st.st_mode & S_IRGRP ? 'r' : '-',
st.st_mode & S_IWGRP ? 'w' : '-',
st.st_mode & S_ISGID ? 's' : (st.st_mode & S_IXGRP ? 'x' : '-'),
st.st_mode & S_IROTH ? 'r' : '-',
st.st_mode & S_IWOTH ? 'w' : '-');
if (st.st_mode & S_ISVTX)
printf("t");
else
printf("%c", st.st_mode & S_IXOTH ? 'x' : '-');
printf(" %u", st.st_nlink);
auto username = users.get(st.st_uid);
if (!flag_print_numeric && username.has_value()) {
printf(" %7s", username.value().characters());
} else {
printf(" %7u", st.st_uid);
}
if (!flag_hide_group) {
auto groupname = groups.get(st.st_gid);
if (!flag_print_numeric && groupname.has_value()) {
printf(" %7s", groupname.value().characters());
} else {
printf(" %7u", st.st_gid);
}
}
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
printf(" %4u,%4u ", major(st.st_rdev), minor(st.st_rdev));
} else {
if (flag_human_readable) {
printf(" %10s ", human_readable_size((size_t)st.st_size).characters());
} else {
printf(" %10zd ", st.st_size);
}
}
printf(" %s ", Core::DateTime::from_timestamp(st.st_mtime).to_string().characters());
print_name(st, name, path.characters(), path.characters());
printf("\n");
return true;
}
static int do_file_system_object_long(const char* path)
{
if (flag_list_directories_only) {
struct stat stat;
int rc = lstat(path, &stat);
if (rc < 0) {
perror("lstat");
memset(&stat, 0, sizeof(stat));
}
if (print_filesystem_object(path, path, stat))
return 0;
return 2;
}
auto flags = Core::DirIterator::SkipDots;
if (flag_show_dotfiles)
flags = Core::DirIterator::Flags::NoFlags;
if (flag_show_almost_all_dotfiles)
flags = Core::DirIterator::SkipParentAndBaseDir;
Core::DirIterator di(path, flags);
if (di.has_error()) {
if (di.error() == ENOTDIR) {
struct stat stat;
int rc = lstat(path, &stat);
if (rc < 0) {
perror("lstat");
memset(&stat, 0, sizeof(stat));
}
if (print_filesystem_object(path, path, stat))
return 0;
return 2;
}
fprintf(stderr, "%s: %s\n", path, di.error_string());
return 1;
}
struct FileMetadata {
String name;
String path;
struct stat stat;
};
Vector<FileMetadata> files;
while (di.has_next()) {
FileMetadata metadata;
metadata.name = di.next_path();
ASSERT(!metadata.name.is_empty());
if (metadata.name.ends_with('~') && flag_ignore_backups && metadata.name != path)
continue;
StringBuilder builder;
builder.append(path);
builder.append('/');
builder.append(metadata.name);
metadata.path = builder.to_string();
ASSERT(!metadata.path.is_null());
int rc = lstat(metadata.path.characters(), &metadata.stat);
if (rc < 0) {
perror("lstat");
memset(&metadata.stat, 0, sizeof(metadata.stat));
}
files.append(move(metadata));
}
quick_sort(files, [](auto& a, auto& b) {
if (flag_sort_by_timestamp) {
if (flag_reverse_sort)
return a.stat.st_mtime > b.stat.st_mtime;
return a.stat.st_mtime < b.stat.st_mtime;
}
// Fine, sort by name then!
if (flag_reverse_sort)
return a.name > b.name;
return a.name < b.name;
});
for (auto& file : files) {
if (!print_filesystem_object(file.path, file.name, file.stat))
return 2;
}
return 0;
}
static bool print_filesystem_object_short(const char* path, const char* name, size_t* nprinted)
{
struct stat st;
int rc = lstat(path, &st);
if (rc == -1) {
printf("lstat(%s) failed: %s\n", path, strerror(errno));
return false;
}
if (flag_show_inode)
printf("%08u ", st.st_ino);
*nprinted = print_name(st, name, nullptr, path);
return true;
}
int do_file_system_object_short(const char* path)
{
if (flag_list_directories_only) {
size_t nprinted = 0;
bool status = print_filesystem_object_short(path, path, &nprinted);
printf("\n");
if (status)
return 0;
return 2;
}
auto flags = Core::DirIterator::SkipDots;
if (flag_show_dotfiles)
flags = Core::DirIterator::Flags::NoFlags;
if (flag_show_almost_all_dotfiles)
flags = Core::DirIterator::SkipParentAndBaseDir;
Core::DirIterator di(path, flags);
if (di.has_error()) {
if (di.error() == ENOTDIR) {
size_t nprinted = 0;
bool status = print_filesystem_object_short(path, path, &nprinted);
printf("\n");
if (status)
return 0;
return 2;
}
fprintf(stderr, "%s: %s\n", path, di.error_string());
return 1;
}
Vector<String> names;
size_t longest_name = 0;
while (di.has_next()) {
String name = di.next_path();
if (name.ends_with('~') && flag_ignore_backups && name != path)
continue;
names.append(name);
if (names.last().length() > longest_name)
longest_name = name.length();
}
quick_sort(names);
size_t printed_on_row = 0;
size_t nprinted = 0;
for (size_t i = 0; i < names.size(); ++i) {
auto& name = names[i];
StringBuilder builder;
builder.append(path);
builder.append('/');
builder.append(name);
if (!print_filesystem_object_short(builder.to_string().characters(), name.characters(), &nprinted))
return 2;
int offset = 0;
if (terminal_columns > longest_name)
offset = terminal_columns % longest_name / (terminal_columns / longest_name);
// The offset must be at least 2 because:
// - With each file an additional char is printed e.g. '@','*'.
// - Each filename must be separated by a space.
size_t column_width = longest_name + max(offset, 2);
printed_on_row += column_width;
for (size_t j = nprinted; i != (names.size() - 1) && j < column_width; ++j)
printf(" ");
if ((printed_on_row + column_width) >= terminal_columns) {
printf("\n");
printed_on_row = 0;
}
}
if (printed_on_row)
printf("\n");
return 0;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/File.h>
#include <stdio.h>
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/proc/interrupts", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
auto proc_interrupts = Core::File::construct("/proc/interrupts");
if (!proc_interrupts->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", proc_interrupts->error_string());
return 1;
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
printf("%4s %-10s\n", " ", "CPU0");
auto file_contents = proc_interrupts->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
json.value().as_array().for_each([](auto& value) {
auto handler = value.as_object();
auto purpose = handler.get("purpose").to_string();
auto interrupt = handler.get("interrupt_line").to_string();
auto controller = handler.get("controller").to_string();
auto call_count = handler.get("call_count").to_string();
printf("%4s: %-10s %-10s %-30s\n",
interrupt.characters(), call_count.characters(), controller.characters(), purpose.characters());
});
return 0;
}

203
Userland/Utilities/lsof.cpp Normal file
View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2020, Maciej Zygmanowski <sppmacd@pm.me>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/GenericLexer.h>
#include <AK/HashMap.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonParser.h>
#include <AK/JsonValue.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/ProcessStatisticsReader.h>
#include <ctype.h>
#include <stdio.h>
struct OpenFile {
int fd;
int pid;
String type;
String name;
String state;
String full_name;
};
static bool parse_name(StringView name, OpenFile& file)
{
GenericLexer lexer(name);
auto component1 = lexer.consume_until(':');
if (lexer.tell_remaining() == 0) {
file.name = component1;
return true;
} else {
file.type = component1;
auto component2 = lexer.consume_while([](char c) { return isprint(c) && !isspace(c) && c != '('; });
lexer.ignore_while(isspace);
file.name = component2;
if (lexer.tell_remaining() == 0) {
return true;
} else {
if (!lexer.consume_specific('(')) {
dbgln("parse_name: expected (");
return false;
}
auto component3 = lexer.consume_until(')');
if (lexer.tell_remaining() != 0) {
dbgln("parse_name: expected EOF");
return false;
}
file.state = component3;
return true;
}
}
}
static Vector<OpenFile> get_open_files_by_pid(pid_t pid)
{
auto file = Core::File::open(String::format("/proc/%d/fds", pid), Core::IODevice::OpenMode::ReadOnly);
if (file.is_error()) {
printf("lsof: PID %d: %s\n", pid, file.error().characters());
return Vector<OpenFile>();
}
auto data = file.value()->read_all();
JsonParser parser(data);
auto result = parser.parse();
if (!result.has_value()) {
ASSERT_NOT_REACHED();
}
Vector<OpenFile> files;
result.value().as_array().for_each([pid, &files](const JsonValue& object) {
OpenFile open_file;
open_file.pid = pid;
open_file.fd = object.as_object().get("fd").to_int();
String name = object.as_object().get("absolute_path").to_string();
ASSERT(parse_name(name, open_file));
open_file.full_name = name;
files.append(open_file);
});
return files;
}
static void display_entry(const OpenFile& file, const Core::ProcessStatistics& statistics)
{
printf("%-28s %4d %4d %-10s %4d %s\n", statistics.name.characters(), file.pid, statistics.pgid, statistics.username.characters(), file.fd, file.full_name.characters());
}
int main(int argc, char* argv[])
{
if (pledge("stdio rpath proc", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/proc", "r") < 0) {
perror("unveil /proc");
return 1;
}
// needed by ProcessStatisticsReader::get_all()
if (unveil("/etc/passwd", "r") < 0) {
perror("unveil /etc/passwd");
return 1;
}
unveil(nullptr, nullptr);
bool arg_all_processes { false };
int arg_fd { -1 };
const char* arg_uid { nullptr };
int arg_uid_int = -1;
int arg_pgid { -1 };
pid_t arg_pid { -1 };
const char* arg_file_name { nullptr };
if (argc == 1)
arg_all_processes = true;
else {
Core::ArgsParser parser;
parser.set_general_help("List open files of a processes. This can mean actual files in the file system, sockets, pipes, etc.");
parser.add_option(arg_pid, "Select by PID", nullptr, 'p', "pid");
parser.add_option(arg_fd, "Select by file descriptor", nullptr, 'd', "fd");
parser.add_option(arg_uid, "Select by login/UID", nullptr, 'u', "login/UID");
parser.add_option(arg_pgid, "Select by process group ID", nullptr, 'g', "PGID");
parser.add_positional_argument(arg_file_name, "File name", "file name", Core::ArgsParser::Required::No);
parser.parse(argc, argv);
}
{
// try convert UID to int
auto arg = String(arg_uid).to_int();
if (arg.has_value())
arg_uid_int = arg.value();
}
printf("%-28s %4s %4s %-10s %4s %s\n", "COMMAND", "PID", "PGID", "USER", "FD", "NAME");
auto processes = Core::ProcessStatisticsReader::get_all();
if (!processes.has_value())
return 1;
if (arg_pid == -1) {
for (auto process : processes.value()) {
if (process.key == 0)
continue;
auto open_files = get_open_files_by_pid(process.key);
if (open_files.is_empty())
continue;
for (auto file : open_files) {
if ((arg_all_processes)
|| (arg_fd != -1 && file.fd == arg_fd)
|| (arg_uid_int != -1 && (int)process.value.uid == arg_uid_int)
|| (arg_uid != nullptr && process.value.username == arg_uid)
|| (arg_pgid != -1 && (int)process.value.pgid == arg_pgid)
|| (arg_file_name != nullptr && file.name == arg_file_name))
display_entry(file, process.value);
}
}
} else {
auto open_files = get_open_files_by_pid(arg_pid);
if (open_files.is_empty())
return 0;
for (auto file : open_files) {
display_entry(file, processes.value().get(arg_pid).value());
}
}
return 0;
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/File.h>
#include <LibPCIDB/Database.h>
#include <stdio.h>
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/res/pci.ids", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/proc/pci", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
auto db = PCIDB::Database::open();
if (!db)
warnln("Couldn't open PCI ID database");
auto proc_pci = Core::File::construct("/proc/pci");
if (!proc_pci->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", proc_pci->error_string());
return 1;
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
auto file_contents = proc_pci->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
json.value().as_array().for_each([db](auto& value) {
auto dev = value.as_object();
auto seg = dev.get("seg").to_u32();
auto bus = dev.get("bus").to_u32();
auto slot = dev.get("slot").to_u32();
auto function = dev.get("function").to_u32();
auto vendor_id = dev.get("vendor_id").to_u32();
auto device_id = dev.get("device_id").to_u32();
auto revision_id = dev.get("revision_id").to_u32();
auto class_id = dev.get("class").to_u32();
String vendor_name;
String device_name;
String class_name;
if (db) {
vendor_name = db->get_vendor(vendor_id);
device_name = db->get_device(vendor_id, device_id);
class_name = db->get_class(class_id);
}
if (vendor_name.is_empty())
vendor_name = String::format("%02x", vendor_id);
if (device_name.is_empty())
device_name = String::format("%02x", device_id);
if (class_name.is_empty())
class_name = String::format("%04x", class_id);
outln("{:04x}:{:02x}:{:02x}.{} {}: {} {} (rev {:02x})", seg, bus, slot, function, class_name, vendor_name, device_name, revision_id);
});
return 0;
}

121
Userland/Utilities/man.cpp Normal file
View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibMarkdown/Document.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int view_width = 0;
if (isatty(STDOUT_FILENO)) {
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0)
view_width = ws.ws_col;
}
if (view_width == 0)
view_width = 80;
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/usr/share/man", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
const char* section = nullptr;
const char* name = nullptr;
Core::ArgsParser args_parser;
args_parser.set_general_help("Read manual pages. Try 'man man' to get started.");
args_parser.add_positional_argument(section, "Section of the man page", "section", Core::ArgsParser::Required::No);
args_parser.add_positional_argument(name, "Name of the man page", "name");
args_parser.parse(argc, argv);
auto make_path = [name](const char* section) {
return String::format("/usr/share/man/man%s/%s.md", section, name);
};
if (!section) {
const char* sections[] = {
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8"
};
for (auto s : sections) {
String path = make_path(s);
if (access(path.characters(), R_OK) == 0) {
section = s;
break;
}
}
if (!section) {
fprintf(stderr, "No man page for %s\n", name);
exit(1);
}
}
auto file = Core::File::construct();
file->set_filename(make_path(section));
if (!file->open(Core::IODevice::OpenMode::ReadOnly)) {
perror("Failed to open man page file");
exit(1);
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
dbgln("Loading man page from {}", file->filename());
auto buffer = file->read_all();
auto source = String::copy(buffer);
printf("%s(%s)\t\tSerenityOS manual\n", name, section);
auto document = Markdown::Document::parse(source);
ASSERT(document);
String rendered = document->render_for_terminal(view_width);
printf("%s", rendered.characters());
}

99
Userland/Utilities/md.cpp Normal file
View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/OwnPtr.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibMarkdown/Document.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
if (pledge("stdio rpath tty", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* file_name = nullptr;
bool html = false;
int view_width = 0;
Core::ArgsParser args_parser;
args_parser.set_general_help("Render Markdown to some other format.");
args_parser.add_option(html, "Render to HTML rather than for the terminal", "html", 'H');
args_parser.add_option(view_width, "Viewport width for the terminal (defaults to current terminal width)", "view-width", 0, "width");
args_parser.add_positional_argument(file_name, "Path to Markdown file", "path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (!html && view_width == 0) {
if (isatty(STDOUT_FILENO)) {
struct winsize ws;
if (ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) < 0)
view_width = 80;
else
view_width = ws.ws_col;
} else {
view_width = 80;
}
}
auto file = Core::File::construct();
bool success;
if (file_name == nullptr) {
success = file->open(STDIN_FILENO, Core::IODevice::OpenMode::ReadOnly, Core::File::ShouldCloseFileDescriptor::No);
} else {
file->set_filename(file_name);
success = file->open(Core::IODevice::OpenMode::ReadOnly);
}
if (!success) {
fprintf(stderr, "Error: %s\n", file->error_string());
return 1;
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
auto buffer = file->read_all();
dbgln("Read size {}", buffer.size());
auto input = String::copy(buffer);
auto document = Markdown::Document::parse(input);
if (!document) {
fprintf(stderr, "Error parsing\n");
return 1;
}
String res = html ? document->render_to_html() : document->render_for_terminal(view_width);
printf("%s", res.characters());
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/EventLoop.h>
#include <LibCore/Timer.h>
#include <stdio.h>
int main(int, char**)
{
Core::EventLoop event_loop;
auto timer = Core::Timer::construct(10, [&] {
dbgln("Now hanging!");
while (true) {
sleep(1);
}
});
return event_loop.exec();
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <AK/StringBuilder.h>
#include <LibCore/ArgsParser.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio cpath rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool create_parents = false;
Vector<const char*> directories;
Core::ArgsParser args_parser;
args_parser.add_option(create_parents, "Create parent directories if they don't exist", "parents", 'p');
args_parser.add_positional_argument(directories, "Directories to create", "directories");
args_parser.parse(argc, argv);
// FIXME: Support -m/--mode option
mode_t mode = 0755;
bool has_errors = false;
for (auto& directory : directories) {
LexicalPath lexical_path(directory);
if (!create_parents) {
if (mkdir(lexical_path.string().characters(), mode) < 0) {
perror("mkdir");
has_errors = true;
}
continue;
}
StringBuilder path_builder;
if (lexical_path.is_absolute())
path_builder.append("/");
for (auto& part : lexical_path.parts()) {
path_builder.append(part);
auto path = path_builder.build();
struct stat st;
if (stat(path.characters(), &st) < 0) {
if (errno != ENOENT) {
perror("stat");
has_errors = true;
break;
}
if (mkdir(path.characters(), mode) < 0) {
perror("mkdir");
has_errors = true;
break;
}
} else {
if (!S_ISDIR(st.st_mode)) {
fprintf(stderr, "mkdir: cannot create directory '%s': not a directory\n", path.characters());
has_errors = true;
break;
}
}
path_builder.append("/");
}
}
return has_errors ? 1 : 0;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio dpath", nullptr) < 0) {
perror("pledge");
return 1;
}
mode_t mode = 0666;
Vector<const char*> paths;
Core::ArgsParser args_parser;
// FIXME: add -m for file modes
args_parser.add_positional_argument(paths, "Paths of FIFOs to create", "paths");
args_parser.parse(argc, argv);
int exit_code = 0;
for (auto path : paths) {
if (mkfifo(path, mode) < 0) {
perror("mkfifo");
exit_code = 1;
}
}
return exit_code;
}

View file

@ -0,0 +1,91 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
constexpr unsigned encoded_device(unsigned major, unsigned minor)
{
return (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
}
static int usage()
{
printf("usage: mknod <name> <c|b|p> [<major> <minor>]\n");
return 0;
}
int main(int argc, char** argv)
{
if (pledge("stdio dpath", nullptr) < 0) {
perror("pledge");
return 1;
}
// FIXME: Add some kind of option for specifying the file permissions.
if (argc < 3)
return usage();
if (argv[2][0] == 'p') {
if (argc != 3)
return usage();
} else if (argc != 5) {
return usage();
}
const char* name = argv[1];
mode_t mode = 0666;
switch (argv[2][0]) {
case 'c':
case 'u':
mode |= S_IFCHR;
break;
case 'b':
mode |= S_IFBLK;
break;
case 'p':
mode |= S_IFIFO;
break;
default:
return usage();
}
int major = 0;
int minor = 0;
if (argc == 5) {
major = atoi(argv[3]);
minor = atoi(argv[4]);
}
int rc = mknod(name, mode, encoded_device(major, minor));
if (rc < 0) {
perror("mknod");
return 1;
}
return 0;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <serenity.h>
#include <string.h>
int main(int argc, char** argv)
{
const char* path = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path to the module to load", "path");
args_parser.parse(argc, argv);
int rc = module_load(path, strlen(path));
if (rc < 0) {
perror("module_load");
return 1;
}
return 0;
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <serenity.h>
#include <string.h>
int main(int argc, char** argv)
{
const char* name = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(name, "Name of the module to unload", "name");
args_parser.parse(argc, argv);
int rc = module_unload(name, strlen(name));
if (rc < 0) {
perror("module_unload");
return 1;
}
return 0;
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
static int key_fd;
static void wait_for_key()
{
printf("\033[7m--[ more ]--\033[0m");
fflush(stdout);
char dummy;
[[maybe_unused]] auto rc = read(key_fd, &dummy, 1);
printf("\n");
}
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio rpath tty", nullptr) < 0) {
perror("pledge");
return 1;
}
key_fd = STDOUT_FILENO;
struct winsize ws;
ioctl(1, TIOCGWINSZ, &ws);
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
unsigned lines_printed = 0;
while (!feof(stdin)) {
char buffer[BUFSIZ];
auto* str = fgets(buffer, sizeof(buffer), stdin);
if (!str)
break;
printf("%s", str);
++lines_printed;
if ((lines_printed % (ws.ws_row - 1)) == 0) {
wait_for_key();
}
}
close(key_fd);
return 0;
}

View file

@ -0,0 +1,219 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonArray.h>
#include <AK/JsonObject.h>
#include <AK/JsonValue.h>
#include <AK/Optional.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static int parse_options(const StringView& options)
{
int flags = 0;
Vector<StringView> parts = options.split_view(',');
for (auto& part : parts) {
if (part == "defaults")
continue;
else if (part == "nodev")
flags |= MS_NODEV;
else if (part == "noexec")
flags |= MS_NOEXEC;
else if (part == "nosuid")
flags |= MS_NOSUID;
else if (part == "bind")
flags |= MS_BIND;
else if (part == "ro")
flags |= MS_RDONLY;
else if (part == "remount")
flags |= MS_REMOUNT;
else
fprintf(stderr, "Ignoring invalid option: %s\n", part.to_string().characters());
}
return flags;
}
static bool is_source_none(const char* source)
{
return !strcmp("none", source);
}
static int get_source_fd(const char* source)
{
if (is_source_none(source))
return -1;
int fd = open(source, O_RDWR);
if (fd < 0)
fd = open(source, O_RDONLY);
if (fd < 0) {
int saved_errno = errno;
auto message = String::format("Failed to open: %s\n", source);
errno = saved_errno;
perror(message.characters());
}
return fd;
}
static bool mount_all()
{
// Mount all filesystems listed in /etc/fstab.
dbgln("Mounting all filesystems...");
auto fstab = Core::File::construct("/etc/fstab");
if (!fstab->open(Core::IODevice::OpenMode::ReadOnly)) {
fprintf(stderr, "Failed to open /etc/fstab: %s\n", fstab->error_string());
return false;
}
bool all_ok = true;
while (fstab->can_read_line()) {
auto line = fstab->read_line();
// Skip comments and blank lines.
if (line.is_empty() || line.starts_with("#"))
continue;
Vector<String> parts = line.split('\t');
if (parts.size() < 3) {
fprintf(stderr, "Invalid fstab entry: %s\n", line.characters());
all_ok = false;
continue;
}
const char* mountpoint = parts[1].characters();
const char* fstype = parts[2].characters();
int flags = parts.size() >= 4 ? parse_options(parts[3]) : 0;
if (strcmp(mountpoint, "/") == 0) {
dbgln("Skipping mounting root");
continue;
}
const char* filename = parts[0].characters();
int fd = get_source_fd(filename);
dbg() << "Mounting " << filename << "(" << fstype << ")"
<< " on " << mountpoint;
int rc = mount(fd, mountpoint, fstype, flags);
if (rc != 0) {
fprintf(stderr, "Failed to mount %s (FD: %d) (%s) on %s: %s\n", filename, fd, fstype, mountpoint, strerror(errno));
all_ok = false;
continue;
}
}
return all_ok;
}
static bool print_mounts()
{
// Output info about currently mounted filesystems.
auto df = Core::File::construct("/proc/df");
if (!df->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Failed to open /proc/df: %s\n", df->error_string());
return false;
}
auto content = df->read_all();
auto json = JsonValue::from_string(content);
ASSERT(json.has_value());
json.value().as_array().for_each([](auto& value) {
auto fs_object = value.as_object();
auto class_name = fs_object.get("class_name").to_string();
auto mount_point = fs_object.get("mount_point").to_string();
auto source = fs_object.get("source").as_string_or("none");
auto readonly = fs_object.get("readonly").to_bool();
auto mount_flags = fs_object.get("mount_flags").to_int();
printf("%s on %s type %s (", source.characters(), mount_point.characters(), class_name.characters());
if (readonly || mount_flags & MS_RDONLY)
printf("ro");
else
printf("rw");
if (mount_flags & MS_NODEV)
printf(",nodev");
if (mount_flags & MS_NOEXEC)
printf(",noexec");
if (mount_flags & MS_NOSUID)
printf(",nosuid");
if (mount_flags & MS_BIND)
printf(",bind");
printf(")\n");
});
return true;
}
int main(int argc, char** argv)
{
const char* source = nullptr;
const char* mountpoint = nullptr;
const char* fs_type = nullptr;
const char* options = nullptr;
bool should_mount_all = false;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(source, "Source path", "source", Core::ArgsParser::Required::No);
args_parser.add_positional_argument(mountpoint, "Mount point", "mountpoint", Core::ArgsParser::Required::No);
args_parser.add_option(fs_type, "File system type", nullptr, 't', "fstype");
args_parser.add_option(options, "Mount options", nullptr, 'o', "options");
args_parser.add_option(should_mount_all, "Mount all file systems listed in /etc/fstab", nullptr, 'a');
args_parser.parse(argc, argv);
if (should_mount_all) {
return mount_all() ? 0 : 1;
}
if (!source && !mountpoint)
return print_mounts() ? 0 : 1;
if (source && mountpoint) {
if (!fs_type)
fs_type = "ext2";
int flags = options ? parse_options(options) : 0;
int fd = get_source_fd(source);
if (mount(fd, mountpoint, fs_type, flags) < 0) {
perror("mount");
return 1;
}
return 0;
}
args_parser.print_usage(stderr, argv[0]);
return 1;
}

94
Userland/Utilities/mv.cpp Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/LexicalPath.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath wpath cpath fattr", nullptr) < 0) {
perror("pledge");
return 1;
}
// NOTE: The "force" option is a dummy for now, it's just here to silence scripts that use "mv -f"
// In the future, it might be used to cancel out an "-i" interactive option.
bool force = false;
bool verbose = false;
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.add_option(force, "Force", "force", 'f');
args_parser.add_option(verbose, "Verbose", "verbose", 'v');
args_parser.add_positional_argument(paths, "Paths to files being moved followed by target location", "paths");
args_parser.parse(argc, argv);
if (paths.size() < 2) {
args_parser.print_usage(stderr, argv[0]);
return 1;
}
auto original_new_path = paths.take_last();
struct stat st;
int rc = lstat(original_new_path, &st);
if (rc != 0 && errno != ENOENT) {
perror("lstat");
return 1;
}
if (paths.size() > 1 && !S_ISDIR(st.st_mode)) {
warnln("Target is not a directory: {}", original_new_path);
return 1;
}
for (auto& old_path : paths) {
String combined_new_path;
const char* new_path = original_new_path;
if (rc == 0 && S_ISDIR(st.st_mode)) {
auto old_basename = LexicalPath(old_path).basename();
combined_new_path = String::format("%s/%s", original_new_path, old_basename.characters());
new_path = combined_new_path.characters();
}
rc = rename(old_path, new_path);
if (rc < 0) {
perror("rename");
return 1;
}
if (verbose)
printf("renamed '%s' -> '%s'\n", old_path, new_path);
}
return 0;
}

234
Userland/Utilities/nc.cpp Normal file
View file

@ -0,0 +1,234 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char** argv)
{
bool should_listen = false;
bool verbose = false;
bool should_close = false;
const char* addr = nullptr;
int port = 0;
Core::ArgsParser args_parser;
args_parser.set_general_help("Network cat: Connect to network sockets as if it were a file.");
args_parser.add_option(should_listen, "Listen instead of connecting", "listen", 'l');
args_parser.add_option(verbose, "Log everything that's happening", "verbose", 'v');
args_parser.add_option(should_close, "Close connection after reading stdin to the end", nullptr, 'N');
args_parser.add_positional_argument(addr, "Address to connect to or listen on", "address");
args_parser.add_positional_argument(port, "Port to connect to or listen on", "port");
args_parser.parse(argc, argv);
int fd;
if (should_listen) {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0) {
perror("socket");
return 1;
}
struct sockaddr_in sa;
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
if (addr) {
if (inet_pton(AF_INET, addr, &sa.sin_addr) < 0) {
perror("inet_pton");
return 1;
}
}
if (bind(listen_fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
perror("bind");
return 1;
}
if (listen(listen_fd, 1) == -1) {
perror("listen");
return 1;
}
char addr_str[100];
struct sockaddr_in sin;
socklen_t len;
len = sizeof(sin);
if (getsockname(listen_fd, (struct sockaddr*)&sin, &len) == -1) {
perror("getsockname");
return 1;
}
if (verbose)
fprintf(stderr, "waiting for a connection on %s:%d\n", inet_ntop(sin.sin_family, &sin.sin_addr, addr_str, sizeof(addr_str) - 1), ntohs(sin.sin_port));
len = sizeof(sin);
fd = accept(listen_fd, (struct sockaddr*)&sin, &len);
if (fd == -1) {
perror("accept");
return 1;
}
if (verbose)
fprintf(stderr, "got connection from %s:%d\n", inet_ntop(sin.sin_family, &sin.sin_addr, addr_str, sizeof(addr_str) - 1), ntohs(sin.sin_port));
if (close(listen_fd) == -1) {
perror("close");
return 1;
};
} else {
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
struct timeval timeout {
3, 0
};
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt");
return 1;
}
if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt");
return 1;
}
char addr_str[100];
struct sockaddr_in dst_addr;
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = htons(port);
if (inet_pton(AF_INET, addr, &dst_addr.sin_addr) < 0) {
perror("inet_pton");
return 1;
}
if (verbose)
fprintf(stderr, "connecting to %s:%d\n", inet_ntop(dst_addr.sin_family, &dst_addr.sin_addr, addr_str, sizeof(addr_str) - 1), ntohs(dst_addr.sin_port));
if (connect(fd, (struct sockaddr*)&dst_addr, sizeof(dst_addr)) < 0) {
perror("connect");
return 1;
}
if (verbose)
fprintf(stderr, "connected!\n");
}
bool stdin_closed = false;
bool fd_closed = false;
fd_set readfds, writefds, exceptfds;
while (!stdin_closed || !fd_closed) {
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&exceptfds);
int highest_fd = 0;
if (!stdin_closed) {
FD_SET(STDIN_FILENO, &readfds);
FD_SET(STDIN_FILENO, &exceptfds);
highest_fd = max(highest_fd, STDIN_FILENO);
}
if (!fd_closed) {
FD_SET(fd, &readfds);
FD_SET(fd, &exceptfds);
highest_fd = max(highest_fd, fd);
}
int ready = select(highest_fd + 1, &readfds, &writefds, &exceptfds, nullptr);
if (ready == -1) {
if (errno == EINTR)
continue;
perror("select");
return 1;
}
if (!stdin_closed && FD_ISSET(STDIN_FILENO, &readfds)) {
char buf[1024];
int nread = read(STDIN_FILENO, buf, sizeof(buf));
if (nread < 0) {
perror("read(STDIN_FILENO)");
return 1;
}
// stdin closed
if (nread == 0) {
stdin_closed = true;
if (verbose)
fprintf(stderr, "stdin closed\n");
if (should_close) {
close(fd);
fd_closed = true;
}
} else if (write(fd, buf, nread) < 0) {
perror("write(fd)");
return 1;
}
}
if (!fd_closed && FD_ISSET(fd, &readfds)) {
char buf[1024];
int nread = read(fd, buf, sizeof(buf));
if (nread < 0) {
perror("read(fd)");
return 1;
}
// remote end closed
if (nread == 0) {
close(STDIN_FILENO);
stdin_closed = true;
fd_closed = true;
if (verbose)
fprintf(stderr, "remote closed\n");
} else if (write(STDOUT_FILENO, buf, nread) < 0) {
perror("write(STDOUT_FILENO)");
return 1;
}
}
}
return 0;
}

117
Userland/Utilities/nl.cpp Normal file
View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <string.h>
enum NumberStyle {
NumberAllLines,
NumberNonEmptyLines,
NumberNoLines,
};
int main(int argc, char** argv)
{
NumberStyle number_style = NumberNonEmptyLines;
int increment = 1;
const char* separator = " ";
int start_number = 1;
int number_width = 6;
Vector<const char*> files;
Core::ArgsParser args_parser;
Core::ArgsParser::Option number_style_option {
true,
"Line numbering style: 't' for non-empty lines, 'a' for all lines, 'n' for no lines",
"body-numbering",
'b',
"style",
[&number_style](const char* s) {
if (!strcmp(s, "t"))
number_style = NumberNonEmptyLines;
else if (!strcmp(s, "a"))
number_style = NumberAllLines;
else if (!strcmp(s, "n"))
number_style = NumberNoLines;
else
return false;
return true;
}
};
args_parser.add_option(move(number_style_option));
args_parser.add_option(increment, "Line count increment", "increment", 'i', "number");
args_parser.add_option(separator, "Separator between line numbers and lines", "separator", 's', "string");
args_parser.add_option(start_number, "Initial line number", "startnum", 'v', "number");
args_parser.add_option(number_width, "Number width", "width", 'w', "number");
args_parser.add_positional_argument(files, "Files to process", "file", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
Vector<FILE*> file_pointers;
if (!files.is_empty()) {
for (auto& file : files) {
FILE* file_pointer = fopen(file, "r");
if (!file_pointer) {
fprintf(stderr, "unable to open %s\n", file);
continue;
}
file_pointers.append(file_pointer);
}
} else {
file_pointers.append(stdin);
}
for (auto& file_pointer : file_pointers) {
int line_number = start_number - increment; // so the line number can start at 1 when added below
int previous_character = 0;
int next_character = 0;
while ((next_character = fgetc(file_pointer)) != EOF) {
if (previous_character == 0 || previous_character == '\n') {
if (next_character == '\n' && number_style != NumberAllLines) {
// Skip printing line count on empty lines.
printf("\n");
continue;
}
if (number_style != NumberNoLines)
printf("%*d%s", number_width, (line_number += increment), separator);
else
printf("%*s", number_width, "");
}
putchar(next_character);
previous_character = next_character;
}
fclose(file_pointer);
if (previous_character != '\n')
printf("\n"); // for cases where files have no trailing newline
}
return 0;
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibGUI/Application.h>
#include <LibGUI/Notification.h>
#include <LibGfx/Bitmap.h>
#include <stdio.h>
int main(int argc, char** argv)
{
auto app = GUI::Application::construct(argc, argv);
Core::ArgsParser args_parser;
const char* title = nullptr;
const char* message = nullptr;
const char* icon_path = nullptr;
args_parser.add_positional_argument(title, "Title of the notification", "title");
args_parser.add_positional_argument(message, "Message to display in the notification", "message");
args_parser.add_positional_argument(icon_path, "Path of icon to display in the notification", "icon-path", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
auto notification = GUI::Notification::construct();
notification->set_text(message);
notification->set_title(title);
notification->set_icon(Gfx::Bitmap::load_from_file(icon_path));
notification->show();
return 0;
}

View file

@ -0,0 +1,344 @@
/*
* Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _BSD_SOURCE
#define _DEFAULT_SOURCE
#include <AK/Endian.h>
#include <AK/Random.h>
#include <LibCore/ArgsParser.h>
#include <arpa/inet.h>
#include <inttypes.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <time.h>
// An NtpTimestamp is a 64-bit integer that's a 32.32 binary-fixed point number.
// The integral part in the upper 32 bits represents seconds since 1900-01-01.
// The fractional part in the lower 32 bits stores fractional bits times 2 ** 32.
typedef uint64_t NtpTimestamp;
struct [[gnu::packed]] NtpPacket {
uint8_t li_vn_mode;
uint8_t stratum;
int8_t poll;
int8_t precision;
uint32_t root_delay;
uint32_t root_dispersion;
uint32_t reference_id;
NtpTimestamp reference_timestamp;
NtpTimestamp origin_timestamp;
NtpTimestamp receive_timestamp;
NtpTimestamp transmit_timestamp;
uint8_t leap_information() const { return li_vn_mode >> 6; }
uint8_t version_number() const { return (li_vn_mode >> 3) & 7; }
uint8_t mode() const { return li_vn_mode & 7; }
};
static_assert(sizeof(NtpPacket) == 48);
// NTP measures time in seconds since 1900-01-01, POSIX in seconds since 1970-01-01.
// 1900 wasn't a leap year, so there are 70/4 leap years between 1900 and 1970.
// Overflows a 32-bit signed int, but not a 32-bit unsigned int.
const unsigned SecondsFrom1900To1970 = (70u * 365u + 70u / 4u) * 24u * 60u * 60u;
static NtpTimestamp ntp_timestamp_from_timeval(const timeval& t)
{
ASSERT(t.tv_usec >= 0 && t.tv_usec < 1'000'000); // Fits in 20 bits when normalized.
// Seconds just need translation to the different origin.
uint32_t seconds = t.tv_sec + SecondsFrom1900To1970;
// Fractional bits are decimal fixed point (*1'000'000) in timeval, but binary fixed-point (* 2**32) in NTP timestamps.
uint32_t fractional_bits = static_cast<uint32_t>((static_cast<uint64_t>(t.tv_usec) << 32) / 1'000'000);
return (static_cast<NtpTimestamp>(seconds) << 32) | fractional_bits;
}
static timeval timeval_from_ntp_timestamp(const NtpTimestamp& ntp_timestamp)
{
timeval t;
t.tv_sec = static_cast<time_t>(ntp_timestamp >> 32) - SecondsFrom1900To1970;
t.tv_usec = static_cast<suseconds_t>((static_cast<uint64_t>(ntp_timestamp & 0xFFFFFFFFu) * 1'000'000) >> 32);
return t;
}
static String format_ntp_timestamp(NtpTimestamp ntp_timestamp)
{
char buffer[28]; // YYYY-MM-DDTHH:MM:SS.UUUUUUZ is 27 characters long.
timeval t = timeval_from_ntp_timestamp(ntp_timestamp);
struct tm tm;
gmtime_r(&t.tv_sec, &tm);
size_t written = strftime(buffer, sizeof(buffer), "%Y-%m-%dT%T.", &tm);
ASSERT(written == 20);
written += snprintf(buffer + written, sizeof(buffer) - written, "%06d", (int)t.tv_usec);
ASSERT(written == 26);
buffer[written++] = 'Z';
buffer[written] = '\0';
return buffer;
}
int main(int argc, char** argv)
{
#ifdef __serenity__
if (pledge("stdio inet dns settime", nullptr) < 0) {
perror("pledge");
return 1;
}
#endif
bool adjust_time = false;
bool set_time = false;
bool verbose = false;
// FIXME: Change to serenityos.pool.ntp.org once https://manage.ntppool.org/manage/vendor/zone?a=km5a8h&id=vz-14154g is approved.
// Other NTP servers:
// - time.nist.gov
// - time.apple.com
// - time.cloudflare.com (has NTS), https://blog.cloudflare.com/secure-time/
// - time.windows.com
//
// Leap seconds smearing NTP servers:
// - time.facebook.com , https://engineering.fb.com/production-engineering/ntp-service/ , sine-smears over 18 hours
// - time.google.com , https://developers.google.com/time/smear , linear-smears over 24 hours
const char* host = "time.google.com";
Core::ArgsParser args_parser;
args_parser.add_option(adjust_time, "Gradually adjust system time (requires root)", "adjust", 'a');
args_parser.add_option(set_time, "Immediately set system time (requires root)", "set", 's');
args_parser.add_option(verbose, "Verbose output", "verbose", 'v');
args_parser.add_positional_argument(host, "NTP server", "host", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (adjust_time && set_time) {
fprintf(stderr, "-a and -s are mutually exclusive\n");
return 1;
}
#ifdef __serenity__
if (!adjust_time && !set_time) {
if (pledge("stdio inet dns", nullptr) < 0) {
perror("pledge");
return 1;
}
}
#endif
auto* hostent = gethostbyname(host);
if (!hostent) {
fprintf(stderr, "Lookup failed for '%s'\n", host);
return 1;
}
#ifdef __serenity__
if (pledge((adjust_time || set_time) ? "stdio inet settime" : "stdio inet", nullptr) < 0) {
perror("pledge");
return 1;
}
unveil(nullptr, nullptr);
#endif
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
perror("socket");
return 1;
}
struct timeval timeout {
5, 0
};
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("setsockopt");
return 1;
}
int enable = 1;
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &enable, sizeof(enable)) < 0) {
perror("setsockopt");
return 1;
}
sockaddr_in peer_address;
memset(&peer_address, 0, sizeof(peer_address));
peer_address.sin_family = AF_INET;
peer_address.sin_port = htons(123);
peer_address.sin_addr.s_addr = *(const in_addr_t*)hostent->h_addr_list[0];
NtpPacket packet;
memset(&packet, 0, sizeof(packet));
packet.li_vn_mode = (4 << 3) | 3; // Version 4, client connection.
// The server will copy the transmit_timestamp to origin_timestamp in the reply.
// To not leak the local time, keep the time we sent the packet locally and
// send random bytes to the server.
auto random_transmit_timestamp = get_random<NtpTimestamp>();
timeval local_transmit_time;
gettimeofday(&local_transmit_time, nullptr);
packet.transmit_timestamp = random_transmit_timestamp;
ssize_t rc;
rc = sendto(fd, &packet, sizeof(packet), 0, (const struct sockaddr*)&peer_address, sizeof(peer_address));
if (rc < 0) {
perror("sendto");
return 1;
}
if ((size_t)rc < sizeof(packet)) {
fprintf(stderr, "incomplete packet send\n");
return 1;
}
iovec iov { &packet, sizeof(packet) };
char control_message_buffer[CMSG_SPACE(sizeof(timeval))];
msghdr msg = { &peer_address, sizeof(peer_address), &iov, 1, control_message_buffer, sizeof(control_message_buffer), 0 };
rc = recvmsg(fd, &msg, 0);
if (rc < 0) {
perror("recvmsg");
return 1;
}
timeval userspace_receive_time;
gettimeofday(&userspace_receive_time, nullptr);
if ((size_t)rc < sizeof(packet)) {
fprintf(stderr, "incomplete packet recv\n");
return 1;
}
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
ASSERT(cmsg->cmsg_level == SOL_SOCKET);
ASSERT(cmsg->cmsg_type == SCM_TIMESTAMP);
ASSERT(!CMSG_NXTHDR(&msg, cmsg));
timeval kernel_receive_time;
memcpy(&kernel_receive_time, CMSG_DATA(cmsg), sizeof(kernel_receive_time));
// Checks 3 and 4 from end of section 5 of rfc4330.
if (packet.version_number() != 3 && packet.version_number() != 4) {
fprintf(stderr, "unexpected version number %d\n", packet.version_number());
return 1;
}
if (packet.mode() != 4) { // 4 means "server", which should be the reply to our 3 ("client") request.
fprintf(stderr, "unexpected mode %d\n", packet.mode());
return 1;
}
if (packet.stratum == 0 || packet.stratum >= 16) {
fprintf(stderr, "unexpected stratum value %d\n", packet.stratum);
return 1;
}
if (packet.origin_timestamp != random_transmit_timestamp) {
fprintf(stderr, "expected %#016" PRIx64 " as origin timestamp, got %#016" PRIx64 "\n", random_transmit_timestamp, packet.origin_timestamp);
return 1;
}
if (packet.transmit_timestamp == 0) {
fprintf(stderr, "got transmit_timestamp 0\n");
return 1;
}
NtpTimestamp origin_timestamp = ntp_timestamp_from_timeval(local_transmit_time);
NtpTimestamp receive_timestamp = be64toh(packet.receive_timestamp);
NtpTimestamp transmit_timestamp = be64toh(packet.transmit_timestamp);
NtpTimestamp destination_timestamp = ntp_timestamp_from_timeval(kernel_receive_time);
timeval kernel_to_userspace_latency;
timersub(&userspace_receive_time, &kernel_receive_time, &kernel_to_userspace_latency);
if (set_time) {
// FIXME: Do all the time filtering described in 5905, or at least correct for time of flight.
timeval t = timeval_from_ntp_timestamp(transmit_timestamp);
if (settimeofday(&t, nullptr) < 0) {
perror("settimeofday");
return 1;
}
}
if (verbose) {
printf("NTP response from %s:\n", inet_ntoa(peer_address.sin_addr));
printf("Leap Information: %d\n", packet.leap_information());
printf("Version Number: %d\n", packet.version_number());
printf("Mode: %d\n", packet.mode());
printf("Stratum: %d\n", packet.stratum);
printf("Poll: %d\n", packet.stratum);
printf("Precision: %d\n", packet.precision);
printf("Root delay: %#x\n", ntohl(packet.root_delay));
printf("Root dispersion: %#x\n", ntohl(packet.root_dispersion));
u32 ref_id = ntohl(packet.reference_id);
printf("Reference ID: %#x", ref_id);
if (packet.stratum == 1) {
printf(" ('%c%c%c%c')", (ref_id & 0xff000000) >> 24, (ref_id & 0xff0000) >> 16, (ref_id & 0xff00) >> 8, ref_id & 0xff);
}
printf("\n");
printf("Reference timestamp: %#016" PRIx64 " (%s)\n", be64toh(packet.reference_timestamp), format_ntp_timestamp(be64toh(packet.reference_timestamp)).characters());
printf("Origin timestamp: %#016" PRIx64 " (%s)\n", origin_timestamp, format_ntp_timestamp(origin_timestamp).characters());
printf("Receive timestamp: %#016" PRIx64 " (%s)\n", receive_timestamp, format_ntp_timestamp(receive_timestamp).characters());
printf("Transmit timestamp: %#016" PRIx64 " (%s)\n", transmit_timestamp, format_ntp_timestamp(transmit_timestamp).characters());
printf("Destination timestamp: %#016" PRIx64 " (%s)\n", destination_timestamp, format_ntp_timestamp(destination_timestamp).characters());
// When the system isn't under load, user-space t and packet_t are identical. If a shell with `yes` is running, it can be as high as 30ms in this program,
// which gets user-space time immediately after the recvmsg() call. In programs that have an event loop reading from multiple sockets, it could be higher.
printf("Receive latency: %" PRId64 ".%06d s\n", (i64)kernel_to_userspace_latency.tv_sec, (int)kernel_to_userspace_latency.tv_usec);
}
// Parts of the "Clock Filter" computations, https://tools.ietf.org/html/rfc5905#section-10
NtpTimestamp T1 = origin_timestamp;
NtpTimestamp T2 = receive_timestamp;
NtpTimestamp T3 = transmit_timestamp;
NtpTimestamp T4 = destination_timestamp;
auto timestamp_difference_in_seconds = [](NtpTimestamp from, NtpTimestamp to) {
return static_cast<int64_t>(to - from) / pow(2.0, 32);
};
// The network round-trip time of the request.
// T4-T1 is the wall clock roundtrip time, in local ticks.
// T3-T2 is the server side processing time, in server ticks.
double delay_s = timestamp_difference_in_seconds(T1, T4) - timestamp_difference_in_seconds(T2, T3);
// The offset from local time to server time, ignoring network delay.
// Both T2-T1 and T3-T4 estimate this; this takes the average of both.
// Or, equivalently, (T1+T4)/2 estimates local time, (T2+T3)/2 estimate server time, this is the difference.
double offset_s = 0.5 * (timestamp_difference_in_seconds(T1, T2) + timestamp_difference_in_seconds(T4, T3));
if (verbose)
printf("Delay: %f\n", delay_s);
printf("Offset: %f\n", offset_s);
if (adjust_time) {
long delta_us = static_cast<long>(round(offset_s * 1'000'000));
timeval delta_timeval;
delta_timeval.tv_sec = delta_us / 1'000'000;
delta_timeval.tv_usec = delta_us % 1'000'000;
if (delta_timeval.tv_usec < 0) {
delta_timeval.tv_sec--;
delta_timeval.tv_usec += 1'000'000;
}
if (adjtime(&delta_timeval, nullptr) < 0) {
perror("adjtime set");
return 1;
}
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/URL.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/File.h>
#include <LibDesktop/Launcher.h>
#include <string.h>
int main(int argc, char* argv[])
{
Core::EventLoop loop;
Vector<const char*> urls_or_paths;
Core::ArgsParser parser;
parser.set_general_help("Open a file or URL by executing the appropriate program.");
parser.add_positional_argument(urls_or_paths, "URL or file path to open", "url-or-path");
parser.parse(argc, argv);
bool all_ok = true;
for (auto& url_or_path : urls_or_paths) {
auto url = URL::create_with_url_or_path(url_or_path);
if (url.protocol() == "file") {
auto real_path = Core::File::real_path_for(url.path());
if (real_path.is_null()) {
// errno *should* be preserved from Core::File::real_path_for().
warnln("Failed to open '{}': {}", url.path(), strerror(errno));
all_ok = false;
continue;
}
url = URL::create_with_url_or_path(real_path);
}
if (!Desktop::Launcher::open(url)) {
warnln("Failed to open '{}'", url);
all_ok = false;
}
}
return all_ok ? 0 : 1;
}

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <LibGUI/Application.h>
#include <LibGUI/Desktop.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int handle_show_all()
{
Core::DirIterator di("/res/wallpapers", Core::DirIterator::SkipDots);
if (di.has_error()) {
fprintf(stderr, "DirIterator: %s\n", di.error_string());
return 1;
}
while (di.has_next()) {
String name = di.next_path();
printf("%s\n", name.characters());
}
return 0;
}
static int handle_show_current()
{
printf("%s\n", GUI::Desktop::the().wallpaper().characters());
return 0;
}
static int handle_set_pape(const String& name)
{
StringBuilder builder;
builder.append("/res/wallpapers/");
builder.append(name);
String path = builder.to_string();
if (!GUI::Desktop::the().set_wallpaper(path)) {
fprintf(stderr, "pape: Failed to set wallpaper %s\n", path.characters());
return 1;
}
return 0;
};
int main(int argc, char** argv)
{
bool show_all = false;
bool show_current = false;
const char* name = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(show_all, "Show all wallpapers", "show-all", 'a');
args_parser.add_option(show_current, "Show current wallpaper", "show-current", 'c');
args_parser.add_positional_argument(name, "Wallpaper to set", "name", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
auto app = GUI::Application::construct(argc, argv);
if (show_all)
return handle_show_all();
else if (show_current)
return handle_show_current();
return handle_set_pape(name);
}

View file

@ -0,0 +1,144 @@
/*
* Copyright (c) 2020, Peter Elliott <pelliott@ualberta.ca>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Types.h>
#include <LibCore/Account.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/GetPassword.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (geteuid() != 0) {
warnln("Not running as root :^(");
return 1;
}
if (pledge("stdio wpath rpath cpath tty id", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/etc/passwd", "rwc") < 0) {
perror("unveil");
return 1;
}
if (unveil("/etc/group", "rwc") < 0) {
perror("unveil");
return 1;
}
if (unveil("/etc/shadow", "rwc") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
bool del = false;
bool lock = false;
bool unlock = false;
const char* username = nullptr;
auto args_parser = Core::ArgsParser();
args_parser.set_general_help("Modify an account password.");
args_parser.add_option(del, "Delete password", "delete", 'd');
args_parser.add_option(lock, "Lock password", "lock", 'l');
args_parser.add_option(unlock, "Unlock password", "unlock", 'u');
args_parser.add_positional_argument(username, "Username", "username", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
uid_t current_uid = getuid();
auto account_or_error = (username)
? Core::Account::from_name(username, Core::Account::OpenPasswdFile::ReadWrite, Core::Account::OpenShadowFile::ReadWrite)
: Core::Account::from_uid(current_uid, Core::Account::OpenPasswdFile::ReadWrite, Core::Account::OpenShadowFile::ReadWrite);
if (account_or_error.is_error()) {
warnln("Core::Account::{}: {}", (username) ? "from_name" : "from_uid", account_or_error.error());
return 1;
}
// Drop privileges after opening all the files through the Core::Account object.
auto gid = getgid();
if (setresgid(gid, gid, gid) < 0) {
perror("setresgid");
return 1;
}
auto uid = getuid();
if (setresuid(uid, uid, uid) < 0) {
perror("setresuid");
return 1;
}
// Make sure /etc/passwd is open and ready for reading, then we can drop a bunch of pledge promises.
setpwent();
if (pledge("stdio tty", nullptr) < 0) {
perror("pledge");
return 1;
}
// target_account is the account we are changing the password of.
auto& target_account = account_or_error.value();
if (current_uid != 0 && current_uid != target_account.uid()) {
warnln("You can't modify passwd for {}", username);
return 1;
}
if (del) {
target_account.delete_password();
} else if (lock) {
target_account.set_password_enabled(false);
} else if (unlock) {
target_account.set_password_enabled(true);
} else {
auto new_password = Core::get_password("New password: ");
if (new_password.is_error()) {
warnln("{}", new_password.error());
return 1;
}
target_account.set_password(new_password.value().characters());
}
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
if (!target_account.sync()) {
perror("Core::Account::Sync");
}
return 0;
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2019-2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibGUI/Application.h>
#include <LibGUI/Clipboard.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
bool print_type = false;
bool no_newline = false;
Core::ArgsParser args_parser;
args_parser.set_general_help("Paste from the clipboard to stdout.");
args_parser.add_option(print_type, "Display the copied type", "print-type", 0);
args_parser.add_option(no_newline, "Do not append a newline", "no-newline", 'n');
args_parser.parse(argc, argv);
auto app = GUI::Application::construct(argc, argv);
auto& clipboard = GUI::Clipboard::the();
auto data_and_type = clipboard.data_and_type();
if (data_and_type.mime_type.is_null()) {
warnln("Nothing copied");
return 1;
}
if (!print_type) {
out("{}", StringView(data_and_type.data));
// Append a newline to text contents, unless the caller says otherwise.
if (data_and_type.mime_type.starts_with("text/") && !no_newline)
outln();
} else {
outln("{}", data_and_type.mime_type);
}
return 0;
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/HashMap.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/ProcessStatisticsReader.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int pid_of(const String& process_name, bool single_shot, bool omit_pid, pid_t pid)
{
bool displayed_at_least_one = false;
auto processes = Core::ProcessStatisticsReader().get_all();
if (!processes.has_value())
return 1;
for (auto& it : processes.value()) {
if (it.value.name == process_name) {
if (!omit_pid || it.value.pid != pid) {
printf(" %d" + (displayed_at_least_one ? 0 : 1), it.value.pid);
displayed_at_least_one = true;
if (single_shot)
break;
}
}
}
if (displayed_at_least_one)
printf("\n");
return 0;
}
int main(int argc, char** argv)
{
bool single_shot = false;
const char* omit_pid_value = nullptr;
const char* process_name = nullptr;
Core::ArgsParser args_parser;
args_parser.add_option(single_shot, "Only return one pid", nullptr, 's');
args_parser.add_option(omit_pid_value, "Omit the given PID, or the parent process if the special value %PPID is passed", nullptr, 'o', "pid");
args_parser.add_positional_argument(process_name, "Process name to search for", "process-name");
args_parser.parse(argc, argv);
pid_t pid_to_omit = 0;
if (omit_pid_value) {
if (!strcmp(omit_pid_value, "%PPID")) {
pid_to_omit = getppid();
} else {
auto number = StringView(omit_pid_value).to_uint();
if (!number.has_value()) {
fprintf(stderr, "Invalid value for -o\n");
args_parser.print_usage(stderr, argv[0]);
return 1;
}
pid_to_omit = number.value();
}
}
return pid_of(process_name, single_shot, omit_pid_value != nullptr, pid_to_omit);
}

242
Userland/Utilities/ping.cpp Normal file
View file

@ -0,0 +1,242 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
static uint16_t internet_checksum(const void* ptr, size_t count)
{
uint32_t checksum = 0;
auto* w = (const uint16_t*)ptr;
while (count > 1) {
checksum += ntohs(*w++);
if (checksum & 0x80000000)
checksum = (checksum & 0xffff) | (checksum >> 16);
count -= 2;
}
while (checksum >> 16)
checksum = (checksum & 0xffff) + (checksum >> 16);
return htons(~checksum);
}
static int total_pings;
static int successful_pings;
static uint32_t total_ms;
static int min_ms;
static int max_ms;
static const char* host;
int main(int argc, char** argv)
{
if (pledge("stdio id inet dns sigaction", nullptr) < 0) {
perror("pledge");
return 1;
}
Core::ArgsParser args_parser;
args_parser.add_positional_argument(host, "Host to ping", "host");
args_parser.parse(argc, argv);
int fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (fd < 0) {
perror("socket");
return 1;
}
if (setgid(getgid()) || setuid(getuid())) {
fprintf(stderr, "Failed to drop privileges.\n");
return 1;
}
if (pledge("stdio inet dns sigaction", nullptr) < 0) {
perror("pledge");
return 1;
}
struct timeval timeout {
1, 0
};
int rc = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
if (rc < 0) {
perror("setsockopt");
return 1;
}
auto* hostent = gethostbyname(host);
if (!hostent) {
printf("Lookup failed for '%s'\n", host);
return 1;
}
if (pledge("stdio inet sigaction", nullptr) < 0) {
perror("pledge");
return 1;
}
pid_t pid = getpid();
sockaddr_in peer_address;
memset(&peer_address, 0, sizeof(peer_address));
peer_address.sin_family = AF_INET;
peer_address.sin_port = 0;
peer_address.sin_addr.s_addr = *(const in_addr_t*)hostent->h_addr_list[0];
struct PingPacket {
struct icmphdr header;
char msg[64 - sizeof(struct icmphdr)];
};
struct PongPacket {
// FIXME: IPv4 headers are not actually fixed-size, handle other sizes.
char ip_header[20];
struct icmphdr header;
char msg[64 - sizeof(struct icmphdr)];
};
uint16_t seq = 1;
sighandler_t ret = signal(SIGINT, [](int) {
int packet_loss = 100;
printf("\n--- %s ping statistics ---\n", host);
if (total_pings)
packet_loss -= 100.0f * successful_pings / total_pings;
printf("%d packets transmitted, %d received, %d%% packet loss\n",
total_pings, successful_pings, packet_loss);
int average_ms = 0;
if (successful_pings)
average_ms = total_ms / successful_pings;
printf("rtt min/avg/max = %d/%d/%d ms\n", min_ms, average_ms, max_ms);
exit(0);
});
if (ret == SIG_ERR) {
perror("failed to install SIGINT handler");
return 1;
}
for (;;) {
PingPacket ping_packet;
memset(&ping_packet, 0, sizeof(PingPacket));
ping_packet.header.type = 8; // Echo request
ping_packet.header.code = 0;
ping_packet.header.un.echo.id = htons(pid);
ping_packet.header.un.echo.sequence = htons(seq++);
bool fits = String("Hello there!\n").copy_characters_to_buffer(ping_packet.msg, sizeof(ping_packet.msg));
// It's a constant string, we can be sure that it fits.
ASSERT(fits);
ping_packet.header.checksum = internet_checksum(&ping_packet, sizeof(PingPacket));
struct timeval tv_send;
gettimeofday(&tv_send, nullptr);
rc = sendto(fd, &ping_packet, sizeof(PingPacket), 0, (const struct sockaddr*)&peer_address, sizeof(sockaddr_in));
if (rc < 0) {
perror("sendto");
return 1;
}
total_pings++;
for (;;) {
PongPacket pong_packet;
socklen_t peer_address_size = sizeof(peer_address);
rc = recvfrom(fd, &pong_packet, sizeof(PongPacket), 0, (struct sockaddr*)&peer_address, &peer_address_size);
if (rc < 0) {
if (errno == EAGAIN) {
printf("Request (seq=%u) timed out.\n", ntohs(ping_packet.header.un.echo.sequence));
break;
}
perror("recvfrom");
return 1;
}
if (pong_packet.header.type != 0)
continue;
if (pong_packet.header.code != 0)
continue;
if (ntohs(pong_packet.header.un.echo.id) != pid)
continue;
struct timeval tv_receive;
gettimeofday(&tv_receive, nullptr);
struct timeval tv_diff;
timersub(&tv_receive, &tv_send, &tv_diff);
int ms = tv_diff.tv_sec * 1000 + tv_diff.tv_usec / 1000;
successful_pings++;
int seq_dif = ntohs(ping_packet.header.un.echo.sequence) - ntohs(pong_packet.header.un.echo.sequence);
// Approximation about the timeout of the out of order packet
if (seq_dif)
ms += seq_dif * 1000 * timeout.tv_sec;
total_ms += ms;
if (min_ms == 0)
min_ms = max_ms = ms;
else if (ms < min_ms)
min_ms = ms;
else if (ms > max_ms)
max_ms = ms;
char addr_buf[64];
printf("Pong from %s: id=%u, seq=%u%s, time=%dms\n",
inet_ntop(AF_INET, &peer_address.sin_addr, addr_buf, sizeof(addr_buf)),
ntohs(pong_packet.header.un.echo.id),
ntohs(pong_packet.header.un.echo.sequence),
pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence ? "(!)" : "",
ms);
// If this was a response to an earlier packet, we still need to wait for the current one.
if (pong_packet.header.un.echo.sequence != ping_packet.header.un.echo.sequence)
continue;
break;
}
sleep(1);
}
return 0;
}

106
Userland/Utilities/pmap.cpp Normal file
View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/ByteBuffer.h>
#include <AK/JsonObject.h>
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <stdio.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/proc", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
const char* pid;
static bool extended = false;
Core::ArgsParser args_parser;
args_parser.add_option(extended, "Extended output", nullptr, 'x');
args_parser.add_positional_argument(pid, "PID", "PID", Core::ArgsParser::Required::Yes);
args_parser.parse(argc, argv);
auto file = Core::File::construct(String::format("/proc/%s/vm", pid));
if (!file->open(Core::IODevice::ReadOnly)) {
fprintf(stderr, "Error: %s\n", file->error_string());
return 1;
}
printf("%s:\n", pid);
if (extended) {
printf("Address Size Resident Dirty Access VMObject Type Purgeable CoW Pages Name\n");
} else {
printf("Address Size Access Name\n");
}
auto file_contents = file->read_all();
auto json = JsonValue::from_string(file_contents);
ASSERT(json.has_value());
json.value().as_array().for_each([](auto& value) {
auto map = value.as_object();
auto address = map.get("address").to_int();
auto size = map.get("size").to_string();
auto readable = map.get("readable").to_bool();
auto writable = map.get("writable").to_bool();
auto executable = map.get("executable").to_bool();
auto access = String::format("%s%s%s", (readable ? "r" : "-"), (writable ? "w" : "-"), (executable ? "x" : "-"));
printf("%08x ", address);
printf("%-10s ", size.characters());
if (extended) {
auto resident = map.get("amount_resident").to_string();
auto dirty = map.get("amount_dirty").to_string();
auto vmobject = map.get("vmobject").to_string();
auto purgeable = map.get("purgeable").to_string();
auto cow_pages = map.get("cow_pages").to_string();
printf("%-10s ", resident.characters());
printf("%-10s ", dirty.characters());
printf("%-6s ", access.characters());
printf("%-22s ", vmobject.characters());
printf("%-10s ", purgeable.characters());
printf("%-12s ", cow_pages.characters());
} else {
printf("%-6s ", access.characters());
}
auto name = map.get("name").to_string();
printf("%-20s ", name.characters());
printf("\n");
});
return 0;
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/PrintfImplementation.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/Types.h>
#include <stdio.h>
#include <unistd.h>
[[gnu::noreturn]] static void fail(const char* message)
{
fputs("\e[31m", stderr);
fputs(message, stderr);
fputs("\e[0m\n", stderr);
exit(1);
}
template<typename PutChFunc, typename ArgumentListRefT, template<typename T, typename U = ArgumentListRefT> typename NextArgument>
struct PrintfImpl : public PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument> {
ALWAYS_INLINE PrintfImpl(PutChFunc& putch, char*& bufptr, const int& nwritten)
: PrintfImplementation::PrintfImpl<PutChFunc, ArgumentListRefT, NextArgument>(putch, bufptr, nwritten)
{
}
ALWAYS_INLINE int format_q(const PrintfImplementation::ModifierState& state, ArgumentListRefT& ap) const
{
auto state_copy = state;
auto str = NextArgument<const char*>()(ap);
if (!str)
str = "(null)";
constexpr auto make_len_or_escape = [](auto str, bool mk_len, size_t field_width, auto putc) {
unsigned len = 2;
if (!mk_len)
putc('"');
for (size_t i = 0; str[i] && (mk_len ? true : (field_width >= len)); ++i) {
auto ch = str[i];
switch (ch) {
case '"':
case '$':
case '\\':
++len;
if (!mk_len)
putc('\\');
}
++len;
if (!mk_len)
putc(ch);
}
if (!mk_len)
putc('"');
return len;
};
auto len = make_len_or_escape(str, true, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
if (!state_copy.dot && (!state_copy.field_width || state_copy.field_width < len))
state_copy.field_width = len;
size_t pad_amount = state_copy.field_width > len ? state_copy.field_width - len : 0;
if (!state_copy.left_pad) {
for (size_t i = 0; i < pad_amount; ++i)
this->m_putch(this->m_bufptr, ' ');
}
make_len_or_escape(str, false, state_copy.field_width, [&](auto c) { this->m_putch(this->m_bufptr, c); });
if (state_copy.left_pad) {
for (size_t i = 0; i < pad_amount; ++i)
this->m_putch(this->m_bufptr, ' ');
}
return state_copy.field_width;
}
};
template<typename T, typename V>
struct ArgvNextArgument {
ALWAYS_INLINE T operator()(V) const
{
static_assert(sizeof(V) != sizeof(V), "Base instantiated");
return declval<T>();
}
};
template<typename V>
struct ArgvNextArgument<char*, V> {
ALWAYS_INLINE char* operator()(V arg) const
{
if (arg.argc == 0)
fail("Not enough arguments");
auto result = *arg.argv++;
--arg.argc;
return result;
}
};
template<typename V>
struct ArgvNextArgument<const char*, V> {
ALWAYS_INLINE const char* operator()(V arg) const
{
if (arg.argc == 0)
return "";
auto result = *arg.argv++;
--arg.argc;
return result;
}
};
template<typename V>
struct ArgvNextArgument<int, V> {
ALWAYS_INLINE int operator()(V arg) const
{
if (arg.argc == 0)
return 0;
auto result = *arg.argv++;
--arg.argc;
return atoi(result);
}
};
template<typename V>
struct ArgvNextArgument<unsigned, V> {
ALWAYS_INLINE unsigned operator()(V arg) const
{
if (arg.argc == 0)
return 0;
auto result = *arg.argv++;
--arg.argc;
return strtoul(result, nullptr, 10);
}
};
template<typename V>
struct ArgvNextArgument<i64, V> {
ALWAYS_INLINE i64 operator()(V arg) const
{
if (arg.argc == 0)
return 0;
auto result = *arg.argv++;
--arg.argc;
return strtoll(result, nullptr, 10);
}
};
template<typename V>
struct ArgvNextArgument<u64, V> {
ALWAYS_INLINE u64 operator()(V arg) const
{
if (arg.argc == 0)
return 0;
auto result = *arg.argv++;
--arg.argc;
return strtoull(result, nullptr, 10);
}
};
template<typename V>
struct ArgvNextArgument<double, V> {
ALWAYS_INLINE double operator()(V arg) const
{
if (arg.argc == 0)
return 0;
auto result = *arg.argv++;
--arg.argc;
return strtod(result, nullptr);
}
};
template<typename V>
struct ArgvNextArgument<int*, V> {
ALWAYS_INLINE int* operator()(V) const
{
ASSERT_NOT_REACHED();
return nullptr;
}
};
struct ArgvWithCount {
char**& argv;
int& argc;
};
static String handle_escapes(const char* string)
{
StringBuilder builder;
for (auto c = *string; c; c = *++string) {
if (c == '\\') {
if (string[1]) {
switch (c = *++string) {
case '\\':
case '"':
builder.append(c);
break;
case 'a':
builder.append('\a');
break;
case 'b':
builder.append('\b');
break;
case 'c':
return builder.build();
case 'e':
builder.append('\e');
break;
case 'f':
builder.append('\f');
break;
case 'n':
builder.append('\n');
break;
case 'r':
builder.append('\r');
break;
case 't':
builder.append('\t');
break;
case 'v':
builder.append('\v');
break;
case 'x':
fail("Unsupported escape '\\x'");
case 'u':
fail("Unsupported escape '\\u'");
case 'U':
fail("Unsupported escape '\\U'");
default:
builder.append(c);
}
} else {
builder.append(c);
}
} else {
builder.append(c);
}
}
return builder.build();
}
int main(int argc, char** argv)
{
if (argc < 2)
return 1;
++argv;
String format = handle_escapes(*(argv++));
auto format_string = format.characters();
argc -= 2;
ArgvWithCount arg { argv, argc };
auto putch = [](auto*, auto ch) {
putchar(ch);
};
auto previous_argc = 0;
do {
previous_argc = argc;
PrintfImplementation::printf_internal<decltype(putch), PrintfImpl, ArgvWithCount, ArgvNextArgument>(putch, nullptr, format_string, arg);
} while (argc && previous_argc != argc);
return 0;
}

297
Userland/Utilities/pro.cpp Normal file
View file

@ -0,0 +1,297 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/FileStream.h>
#include <AK/GenericLexer.h>
#include <AK/LexicalPath.h>
#include <AK/NumberFormat.h>
#include <AK/SharedBuffer.h>
#include <AK/URL.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/File.h>
#include <LibProtocol/Client.h>
#include <LibProtocol/Download.h>
#include <ctype.h>
#include <stdio.h>
// FIXME: Move this somewhere else when it's needed (e.g. in the Browser)
class ContentDispositionParser {
public:
ContentDispositionParser(const StringView& value)
{
GenericLexer lexer(value);
lexer.ignore_while(isspace);
if (lexer.consume_specific("inline")) {
m_kind = Kind::Inline;
if (!lexer.is_eof())
m_might_be_wrong = true;
return;
}
if (lexer.consume_specific("attachment")) {
m_kind = Kind::Attachment;
if (lexer.consume_specific(";")) {
lexer.ignore_while(isspace);
if (lexer.consume_specific("filename=")) {
// RFC 2183: "A short (length <= 78 characters)
// parameter value containing only non-`tspecials' characters SHOULD be
// represented as a single `token'."
// Some people seem to take this as generic advice of "if it doesn't have special characters,
// it's safe to specify as a single token"
// So let's just be as lenient as possible.
if (lexer.next_is('"'))
m_filename = lexer.consume_quoted_string();
else
m_filename = lexer.consume_until(is_any_of("()<>@,;:\\\"/[]?= "));
} else {
m_might_be_wrong = true;
}
}
return;
}
if (lexer.consume_specific("form-data")) {
m_kind = Kind::FormData;
while (lexer.consume_specific(";")) {
lexer.ignore_while(isspace);
if (lexer.consume_specific("name=")) {
m_name = lexer.consume_quoted_string();
} else if (lexer.consume_specific("filename=")) {
if (lexer.next_is('"'))
m_filename = lexer.consume_quoted_string();
else
m_filename = lexer.consume_until(is_any_of("()<>@,;:\\\"/[]?= "));
} else {
m_might_be_wrong = true;
}
}
return;
}
// FIXME: Support 'filename*'
m_might_be_wrong = true;
}
enum class Kind {
Inline,
Attachment,
FormData,
};
const StringView& filename() const { return m_filename; }
const StringView& name() const { return m_name; }
Kind kind() const { return m_kind; }
bool might_be_wrong() const { return m_might_be_wrong; }
private:
StringView m_filename;
StringView m_name;
Kind m_kind { Kind::Inline };
bool m_might_be_wrong { false };
};
template<typename ConditionT>
class ConditionalOutputFileStream final : public OutputFileStream {
public:
template<typename... Args>
ConditionalOutputFileStream(ConditionT&& condition, Args... args)
: OutputFileStream(args...)
, m_condition(condition)
{
}
~ConditionalOutputFileStream()
{
if (!m_condition())
return;
if (!m_buffer.is_empty()) {
OutputFileStream::write(m_buffer);
m_buffer.clear();
}
}
private:
size_t write(ReadonlyBytes bytes) override
{
if (!m_condition()) {
write_to_buffer:;
m_buffer.append(bytes.data(), bytes.size());
return bytes.size();
}
if (!m_buffer.is_empty()) {
auto size = OutputFileStream::write(m_buffer);
m_buffer = m_buffer.slice(size, m_buffer.size() - size);
}
if (!m_buffer.is_empty())
goto write_to_buffer;
return OutputFileStream::write(bytes);
}
ConditionT m_condition;
ByteBuffer m_buffer;
};
int main(int argc, char** argv)
{
const char* url_str = nullptr;
bool save_at_provided_name = false;
const char* data = nullptr;
String method = "GET";
HashMap<String, String, CaseInsensitiveStringTraits> request_headers;
Core::ArgsParser args_parser;
args_parser.set_general_help(
"Download a file from an arbitrary URL. This command uses ProtocolServer, "
"and thus supports at least http, https, and gemini.");
args_parser.add_option(save_at_provided_name, "Write to a file named as the remote file", nullptr, 'O');
args_parser.add_option(data, "(HTTP only) Send the provided data via an HTTP POST request", "data", 'd', "data");
args_parser.add_option(Core::ArgsParser::Option {
.requires_argument = true,
.help_string = "Add a header entry to the request",
.long_name = "header",
.short_name = 'H',
.value_name = "header-value",
.accept_value = [&](auto* s) {
StringView header { s };
auto split = header.find_first_of(':');
if (!split.has_value())
return false;
request_headers.set(header.substring_view(0, split.value()), header.substring_view(split.value() + 1));
return true;
} });
args_parser.add_positional_argument(url_str, "URL to download from", "url");
args_parser.parse(argc, argv);
if (data) {
method = "POST";
// FIXME: Content-Type?
}
URL url(url_str);
if (!url.is_valid()) {
fprintf(stderr, "'%s' is not a valid URL\n", url_str);
return 1;
}
Core::EventLoop loop;
auto protocol_client = Protocol::Client::construct();
auto download = protocol_client->start_download(method, url.to_string(), request_headers, data ? StringView { data }.bytes() : ReadonlyBytes {});
if (!download) {
fprintf(stderr, "Failed to start download for '%s'\n", url_str);
return 1;
}
u32 previous_downloaded_size { 0 };
timeval prev_time, current_time, time_diff;
gettimeofday(&prev_time, nullptr);
bool received_actual_headers = false;
download->on_progress = [&](Optional<u32> maybe_total_size, u32 downloaded_size) {
fprintf(stderr, "\r\033[2K");
if (maybe_total_size.has_value()) {
fprintf(stderr, "\033]9;%d;%d;\033\\", downloaded_size, maybe_total_size.value());
fprintf(stderr, "Download progress: %s / %s", human_readable_size(downloaded_size).characters(), human_readable_size(maybe_total_size.value()).characters());
} else {
fprintf(stderr, "Download progress: %s / ???", human_readable_size(downloaded_size).characters());
}
gettimeofday(&current_time, nullptr);
timersub(&current_time, &prev_time, &time_diff);
auto time_diff_ms = time_diff.tv_sec * 1000 + time_diff.tv_usec / 1000;
auto size_diff = downloaded_size - previous_downloaded_size;
fprintf(stderr, " at %s/s", human_readable_size(((float)size_diff / (float)time_diff_ms) * 1000).characters());
previous_downloaded_size = downloaded_size;
prev_time = current_time;
};
if (save_at_provided_name) {
download->on_headers_received = [&](auto& response_headers, auto status_code) {
if (received_actual_headers)
return;
dbgln("Received headers! response code = {}", status_code.value_or(0));
received_actual_headers = true; // And not trailers!
String output_name;
if (auto content_disposition = response_headers.get("Content-Disposition"); content_disposition.has_value()) {
auto& value = content_disposition.value();
ContentDispositionParser parser(value);
output_name = parser.filename();
}
if (output_name.is_empty())
output_name = url.path();
LexicalPath path { output_name };
output_name = path.basename();
// The URL didn't have a name component, e.g. 'serenityos.org'
if (output_name.is_empty() || output_name == "/") {
int i = -1;
do {
output_name = url.host();
if (i > -1)
output_name = String::format("%s.%d", output_name.characters(), i);
++i;
} while (Core::File::exists(output_name));
}
if (freopen(output_name.characters(), "w", stdout) == nullptr) {
perror("freopen");
loop.quit(1);
return;
}
};
}
download->on_finish = [&](bool success, auto) {
fprintf(stderr, "\033]9;-1;\033\\");
fprintf(stderr, "\n");
if (!success)
fprintf(stderr, "Download failed :(\n");
loop.quit(0);
};
auto output_stream = ConditionalOutputFileStream { [&] { return save_at_provided_name ? received_actual_headers : true; }, stdout };
download->stream_into(output_stream);
dbgln("started download with id {}", download->id());
auto rc = loop.exec();
// FIXME: This shouldn't be needed.
fclose(stdout);
return rc;
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <serenity.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char** argv)
{
Core::ArgsParser args_parser;
const char* pid_argument = nullptr;
const char* cmd_argument = nullptr;
bool enable = false;
bool disable = false;
args_parser.add_option(pid_argument, "Target PID", nullptr, 'p', "PID");
args_parser.add_option(enable, "Enable", nullptr, 'e');
args_parser.add_option(disable, "Disable", nullptr, 'd');
args_parser.add_option(cmd_argument, "Command", nullptr, 'c', "command");
args_parser.parse(argc, argv);
if (!pid_argument && !cmd_argument) {
args_parser.print_usage(stdout, argv[0]);
return 0;
}
if (pid_argument) {
if (!(enable ^ disable)) {
fprintf(stderr, "-p <PID> requires -e xor -d.\n");
return 1;
}
pid_t pid = atoi(pid_argument);
if (enable) {
if (profiling_enable(pid) < 0) {
perror("profiling_enable");
return 1;
}
return 0;
}
if (profiling_disable(pid) < 0) {
perror("profiling_disable");
return 1;
}
return 0;
}
auto cmd_parts = String(cmd_argument).split(' ');
Vector<const char*> cmd_argv;
for (auto& part : cmd_parts)
cmd_argv.append(part.characters());
cmd_argv.append(nullptr);
dbgln("Enabling profiling for PID {}", getpid());
profiling_enable(getpid());
if (execvp(cmd_argv[0], const_cast<char**>(cmd_argv.data())) < 0) {
perror("execv");
return 1;
}
return 0;
}

159
Userland/Utilities/ps.cpp Normal file
View file

@ -0,0 +1,159 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/ProcessStatisticsReader.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath tty", nullptr) < 0) {
perror("pledge");
return 1;
}
String this_tty = ttyname(STDIN_FILENO);
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil("/proc/all", "r") < 0) {
perror("unveil");
return 1;
}
if (unveil("/etc/passwd", "r") < 0) {
perror("unveil");
return 1;
}
unveil(nullptr, nullptr);
enum class Alignment {
Left,
Right,
};
struct Column {
String title;
Alignment alignment { Alignment::Left };
int width { 0 };
String buffer;
};
bool every_process_flag = false;
bool full_format_flag = false;
Core::ArgsParser args_parser;
args_parser.add_option(every_process_flag, "Show every process", nullptr, 'e');
args_parser.add_option(full_format_flag, "Full format", nullptr, 'f');
args_parser.parse(argc, argv);
Vector<Column> columns;
int uid_column = -1;
int pid_column = -1;
int ppid_column = -1;
int state_column = -1;
int tty_column = -1;
int cmd_column = -1;
auto add_column = [&](auto title, auto alignment, auto width) {
columns.append({ title, alignment, width, {} });
return columns.size() - 1;
};
if (full_format_flag) {
uid_column = add_column("UID", Alignment::Left, 9);
pid_column = add_column("PID", Alignment::Right, 5);
ppid_column = add_column("PPID", Alignment::Right, 5);
state_column = add_column("STATE", Alignment::Left, 12);
tty_column = add_column("TTY", Alignment::Left, 6);
cmd_column = add_column("CMD", Alignment::Left, 0);
} else {
pid_column = add_column("PID", Alignment::Right, 5);
tty_column = add_column("TTY", Alignment::Left, 6);
cmd_column = add_column("CMD", Alignment::Left, 0);
}
auto print_column = [](auto& column, auto& string) {
if (!column.width) {
printf("%s", string.characters());
return;
}
if (column.alignment == Alignment::Right)
printf("%*s ", column.width, string.characters());
else
printf("%-*s ", column.width, string.characters());
};
for (auto& column : columns)
print_column(column, column.title);
printf("\n");
auto all_processes = Core::ProcessStatisticsReader::get_all();
if (!all_processes.has_value())
return 1;
for (const auto& it : all_processes.value()) {
const auto& proc = it.value;
auto tty = proc.tty;
if (!every_process_flag && tty != this_tty)
continue;
if (tty.starts_with("/dev/"))
tty = tty.characters() + 5;
else
tty = "n/a";
auto* state = proc.threads.is_empty() ? "Zombie" : proc.threads.first().state.characters();
if (uid_column != -1)
columns[uid_column].buffer = proc.username;
if (pid_column != -1)
columns[pid_column].buffer = String::number(proc.pid);
if (ppid_column != -1)
columns[ppid_column].buffer = String::number(proc.ppid);
if (tty_column != -1)
columns[tty_column].buffer = tty;
if (state_column != -1)
columns[state_column].buffer = state;
if (cmd_column != -1)
columns[cmd_column].buffer = proc.name;
for (auto& column : columns)
print_column(column, column.buffer);
printf("\n");
}
return 0;
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <Kernel/API/Syscall.h>
#include <LibCore/ArgsParser.h>
#include <serenity.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
int mode = 0;
bool purge_all_volatile = false;
bool purge_all_clean_inode = false;
Core::ArgsParser args_parser;
args_parser.add_option(purge_all_volatile, "Mode PURGE_ALL_VOLATILE", nullptr, 'v');
args_parser.add_option(purge_all_clean_inode, "Mode PURGE_ALL_CLEAN_INODE", nullptr, 'c');
args_parser.parse(argc, argv);
if (!purge_all_volatile && !purge_all_clean_inode)
purge_all_volatile = purge_all_clean_inode = true;
if (purge_all_volatile)
mode |= PURGE_ALL_VOLATILE;
if (purge_all_clean_inode)
mode |= PURGE_ALL_CLEAN_INODE;
int purged_page_count = purge(mode);
if (purged_page_count < 0) {
perror("purge");
return 1;
}
printf("Purged page count: %d\n", purged_page_count);
return 0;
}

View file

@ -0,0 +1,452 @@
/*
* Copyright (c) 2020, the SerenityOS developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/MappedFile.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <LibELF/Image.h>
#include <LibELF/Validation.h>
#include <ctype.h>
#include <stdio.h>
static const char* object_file_type_to_string(Elf32_Half type)
{
switch (type) {
case ET_NONE:
return "None";
case ET_REL:
return "Relocatable";
case ET_EXEC:
return "Executable";
case ET_DYN:
return "Shared object";
case ET_CORE:
return "Core";
default:
return "(?)";
}
}
static const char* object_machine_type_to_string(Elf32_Half type)
{
switch (type) {
case ET_NONE:
return "None";
case EM_M32:
return "AT&T WE 32100";
case EM_SPARC:
return "SPARC";
case EM_386:
return "Intel 80386";
case EM_68K:
return "Motorola 68000";
case EM_88K:
return "Motorola 88000";
case EM_486:
return "Intel 80486";
case EM_860:
return "Intel 80860";
case EM_MIPS:
return "MIPS R3000 Big-Endian only";
default:
return "(?)";
}
}
static const char* object_program_header_type_to_string(Elf32_Word type)
{
switch (type) {
case PT_NULL:
return "NULL";
case PT_LOAD:
return "LOAD";
case PT_DYNAMIC:
return "DYNAMIC";
case PT_INTERP:
return "INTERP";
case PT_NOTE:
return "NOTE";
case PT_SHLIB:
return "SHLIB";
case PT_PHDR:
return "PHDR";
case PT_TLS:
return "TLS";
case PT_LOOS:
return "LOOS";
case PT_HIOS:
return "HIOS";
case PT_LOPROC:
return "LOPROC";
case PT_HIPROC:
return "HIPROC";
case PT_GNU_EH_FRAME:
return "GNU_EH_FRAME";
case PT_GNU_RELRO:
return "GNU_RELRO";
case PT_GNU_STACK:
return "GNU_STACK";
case PT_OPENBSD_RANDOMIZE:
return "OPENBSD_RANDOMIZE";
case PT_OPENBSD_WXNEEDED:
return "OPENBSD_WXNEEDED";
case PT_OPENBSD_BOOTDATA:
return "OPENBSD_BOOTDATA";
default:
return "(?)";
}
}
static const char* object_section_header_type_to_string(Elf32_Word type)
{
switch (type) {
case SHT_NULL:
return "NULL";
case SHT_PROGBITS:
return "PROGBITS";
case SHT_SYMTAB:
return "SYMTAB";
case SHT_STRTAB:
return "STRTAB";
case SHT_RELA:
return "RELA";
case SHT_HASH:
return "HASH";
case SHT_DYNAMIC:
return "DYNAMIC";
case SHT_NOTE:
return "NOTE";
case SHT_NOBITS:
return "NOBITS";
case SHT_REL:
return "REL";
case SHT_SHLIB:
return "SHLIB";
case SHT_DYNSYM:
return "DYNSYM";
case SHT_NUM:
return "NUM";
case SHT_INIT_ARRAY:
return "INIT_ARRAY";
case SHT_FINI_ARRAY:
return "FINI_ARRAY";
case SHT_PREINIT_ARRAY:
return "PREINIT_ARRAY";
case SHT_GROUP:
return "GROUP";
case SHT_SYMTAB_SHNDX:
return "SYMTAB_SHNDX";
case SHT_LOOS:
return "SOOS";
case SHT_SUNW_dof:
return "SUNW_dof";
case SHT_GNU_LIBLIST:
return "GNU_LIBLIST";
case SHT_SUNW_move:
return "SUNW_move";
case SHT_SUNW_syminfo:
return "SUNW_syminfo";
case SHT_SUNW_verdef:
return "SUNW_verdef";
case SHT_SUNW_verneed:
return "SUNW_verneed";
case SHT_SUNW_versym: // or SHT_HIOS
return "SUNW_versym";
case SHT_LOPROC:
return "LOPROC";
case SHT_HIPROC:
return "HIPROC";
case SHT_LOUSER:
return "LOUSER";
case SHT_HIUSER:
return "HIUSER";
case SHT_GNU_HASH:
return "GNU_HASH";
default:
return "(?)";
}
}
static const char* object_symbol_type_to_string(Elf32_Word type)
{
switch (type) {
case STT_NOTYPE:
return "NOTYPE";
case STT_OBJECT:
return "OBJECT";
case STT_FUNC:
return "FUNC";
case STT_SECTION:
return "SECTION";
case STT_FILE:
return "FILE";
case STT_TLS:
return "TLS";
case STT_LOPROC:
return "LOPROC";
case STT_HIPROC:
return "HIPROC";
default:
return "(?)";
}
}
static const char* object_symbol_binding_to_string(Elf32_Word type)
{
switch (type) {
case STB_LOCAL:
return "LOCAL";
case STB_GLOBAL:
return "GLOBAL";
case STB_WEAK:
return "WEAK";
case STB_NUM:
return "NUM";
case STB_LOPROC:
return "LOPROC";
case STB_HIPROC:
return "HIPROC";
default:
return "(?)";
}
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path;
static bool display_all = false;
static bool display_elf_header = false;
static bool display_program_headers = false;
static bool display_section_headers = false;
static bool display_headers = false;
static bool display_symbol_table = false;
static bool display_dynamic_symbol_table = false;
static bool display_core_notes = false;
static bool display_relocations = false;
static bool display_unwind_info = false;
static bool display_dynamic_section = false;
Core::ArgsParser args_parser;
args_parser.add_option(display_all, "Display all", "all", 'a');
args_parser.add_option(display_elf_header, "Display ELF header", "file-header", 'h');
args_parser.add_option(display_program_headers, "Display program headers", "program-headers", 'l');
args_parser.add_option(display_section_headers, "Display section headers", "section-headers", 'S');
args_parser.add_option(display_headers, "Equivalent to: -h -l -S", "headers", 'e');
args_parser.add_option(display_symbol_table, "Display the symbol table", "syms", 's');
args_parser.add_positional_argument(path, "ELF path", "path");
args_parser.parse(argc, argv);
if (argc < 3) {
args_parser.print_usage(stderr, argv[0]);
return -1;
}
if (display_headers) {
display_elf_header = true;
display_program_headers = true;
display_section_headers = true;
}
if (display_all) {
display_elf_header = true;
display_program_headers = true;
display_section_headers = true;
display_symbol_table = true;
}
auto file_or_error = MappedFile::map(path);
if (file_or_error.is_error()) {
warnln("Unable to map file {}: {}", path, file_or_error.error());
return -1;
}
auto elf_image_data = file_or_error.value()->bytes();
ELF::Image executable_elf(elf_image_data);
if (!executable_elf.is_valid()) {
fprintf(stderr, "File is not a valid ELF object\n");
return -1;
}
String interpreter_path;
if (!ELF::validate_program_headers(*(const Elf32_Ehdr*)elf_image_data.data(), elf_image_data.size(), (const u8*)elf_image_data.data(), elf_image_data.size(), &interpreter_path)) {
fprintf(stderr, "Invalid ELF headers\n");
return -1;
}
if (executable_elf.is_dynamic() && interpreter_path.is_null()) {
fprintf(stderr, "Warning: Dynamic ELF object has no interpreter path\n");
}
ELF::Image interpreter_image(elf_image_data);
if (!interpreter_image.is_valid()) {
fprintf(stderr, "ELF image is invalid\n");
return -1;
}
auto& header = *reinterpret_cast<const Elf32_Ehdr*>(elf_image_data.data());
if (display_elf_header) {
printf("ELF header:\n");
String magic = String::format("%s", header.e_ident);
printf(" Magic: ");
for (size_t i = 0; i < magic.length(); i++) {
if (isprint(magic[i])) {
printf("%c ", magic[i]);
} else {
printf("%02x ", magic[i]);
}
}
printf("\n");
printf(" Type: %d (%s)\n", header.e_type, object_file_type_to_string(header.e_type));
printf(" Machine: %u (%s)\n", header.e_machine, object_machine_type_to_string(header.e_machine));
printf(" Version: 0x%x\n", header.e_version);
printf(" Entry point address: 0x%x\n", header.e_entry);
printf(" Start of program headers: %u (bytes into file)\n", header.e_phoff);
printf(" Start of section headers: %u (bytes into file)\n", header.e_shoff);
printf(" Flags: 0x%x\n", header.e_flags);
printf(" Size of this header: %u (bytes)\n", header.e_ehsize);
printf(" Size of program headers: %u (bytes)\n", header.e_phentsize);
printf(" Number of program headers: %u\n", header.e_phnum);
printf(" Size of section headers: %u (bytes)\n", header.e_shentsize);
printf(" Number of section headers: %u\n", header.e_shnum);
printf(" Section header string table index: %u\n", header.e_shstrndx);
printf("\n");
}
if (display_section_headers) {
if (!display_all) {
printf("There are %u section headers, starting at offset 0x%x:\n", header.e_shnum, header.e_shoff);
printf("\n");
}
if (!interpreter_image.section_count()) {
printf("There are no sections in this file.\n");
} else {
printf("Section Headers:\n");
printf(" Name Type Address Offset Size Flags\n");
interpreter_image.for_each_section([](const ELF::Image::Section& section) {
printf(" %-15s ", section.name().characters_without_null_termination());
printf("%-15s ", object_section_header_type_to_string(section.type()));
printf("%08x ", section.address());
printf("%08x ", section.offset());
printf("%08x ", section.size());
printf("%u", section.flags());
printf("\n");
return IterationDecision::Continue;
});
}
printf("\n");
}
if (display_program_headers) {
if (!display_all) {
printf("Elf file type is %d (%s)\n", header.e_type, object_file_type_to_string(header.e_type));
printf("Entry point 0x%x\n", header.e_entry);
printf("There are %u program headers, starting at offset %u\n", header.e_phnum, header.e_phoff);
printf("\n");
}
printf("Program Headers:\n");
printf(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align\n");
interpreter_image.for_each_program_header([](const ELF::Image::ProgramHeader& program_header) {
printf(" %-14s ", object_program_header_type_to_string(program_header.type()));
printf("0x%08x ", program_header.offset());
printf("%p ", program_header.vaddr().as_ptr());
printf("%p ", program_header.vaddr().as_ptr()); // FIXME: assumes PhysAddr = VirtAddr
printf("0x%08x ", program_header.size_in_image());
printf("0x%08x ", program_header.size_in_memory());
printf("%04x ", program_header.flags());
printf("0x%08x", program_header.alignment());
printf("\n");
if (program_header.type() == PT_INTERP)
printf(" [Interpreter: %s]\n", program_header.raw_data());
return IterationDecision::Continue;
});
printf("\n");
}
if (display_dynamic_section) {
// TODO: Dynamic section
}
if (display_relocations) {
// TODO: Relocations section
}
if (display_unwind_info) {
// TODO: Unwind info
}
if (display_core_notes) {
// TODO: Core notes
}
if (display_dynamic_symbol_table || display_symbol_table) {
// TODO: dynamic symbol table
if (!interpreter_image.symbol_count()) {
printf("Dynamic symbol information is not available for displaying symbols.\n");
printf("\n");
}
}
if (display_symbol_table) {
if (interpreter_image.symbol_count()) {
printf("Symbol table '.symtab' contains %u entries:\n", interpreter_image.symbol_count());
printf(" Num: Value Size Type Bind Name\n");
interpreter_image.for_each_symbol([](const ELF::Image::Symbol& sym) {
printf(" %4u: ", sym.index());
printf("%08x ", sym.value());
printf("%08x ", sym.size());
printf("%-8s ", object_symbol_type_to_string(sym.type()));
printf("%-8s ", object_symbol_binding_to_string(sym.bind()));
printf("%s", sym.name().characters_without_null_termination());
printf("\n");
return IterationDecision::Continue;
});
}
printf("\n");
}
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020, Sergey Bugaev <bugaevc@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <stdio.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool no_newline = false;
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.add_option(no_newline, "Do not append a newline", "no-newline", 'n');
args_parser.add_positional_argument(paths, "Symlink path", "path");
args_parser.parse(argc, argv);
for (const char* path : paths) {
auto destination = Core::File::read_link(path);
if (destination.is_null()) {
perror(path);
return 1;
}
printf("%s", destination.characters());
if (!no_newline)
putchar('\n');
}
return 0;
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path;
Core::ArgsParser args_parser;
args_parser.set_general_help(
"Show the 'real' path of a file, by resolving all symbolic links along the way.");
args_parser.add_positional_argument(path, "Path to resolve", "path");
args_parser.parse(argc, argv);
char* value = realpath(path, nullptr);
if (value == nullptr) {
perror("realpath");
return 1;
}
printf("%s\n", value);
free(value);
return 0;
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
int main(int, char**)
{
if (reboot() < 0) {
perror("reboot");
return 1;
}
return 0;
}

102
Userland/Utilities/rm.cpp Normal file
View file

@ -0,0 +1,102 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/Vector.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DirIterator.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static int remove(bool recursive, bool force, String path)
{
struct stat path_stat;
if (lstat(path.characters(), &path_stat) < 0) {
if (!force)
perror("lstat");
return force ? 0 : 1;
}
if (S_ISDIR(path_stat.st_mode) && recursive) {
auto di = Core::DirIterator(path, Core::DirIterator::SkipParentAndBaseDir);
if (di.has_error()) {
if (!force)
fprintf(stderr, "DirIterator: %s\n", di.error_string());
return 1;
}
while (di.has_next()) {
int s = remove(true, force, di.next_full_path());
if (s != 0 && !force)
return s;
}
int s = rmdir(path.characters());
if (s < 0 && !force) {
perror("rmdir");
return 1;
}
} else {
int rc = unlink(path.characters());
if (rc < 0 && !force) {
perror("unlink");
return 1;
}
}
return 0;
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath cpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool recursive = false;
bool force = false;
bool verbose = false;
Vector<const char*> paths;
Core::ArgsParser args_parser;
args_parser.add_option(recursive, "Delete directories recursively", "recursive", 'r');
args_parser.add_option(force, "Force", "force", 'f');
args_parser.add_option(verbose, "Verbose", "verbose", 'v');
args_parser.add_positional_argument(paths, "Path(s) to remove", "path");
args_parser.parse(argc, argv);
int rc = 0;
for (auto& path : paths) {
rc |= remove(recursive, force, path);
if (verbose && rc == 0)
printf("removed '%s'\n", path);
}
return rc;
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (pledge("stdio cpath", nullptr) < 0) {
perror("pledge");
return 1;
}
const char* path;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Directory to remove", "path");
args_parser.parse(argc, argv);
int rc = rmdir(path);
if (rc < 0) {
perror("rmdir");
return 1;
}
return 0;
}

129
Userland/Utilities/seq.cpp Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (c) 2020, Nico Weber <thakis@chromium.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/StdLibExtras.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
const char* const g_usage = R"(Usage:
seq [-h|--help]
seq LAST
seq FIRST LAST
seq FIRST INCREMENT LAST
)";
static void print_usage(FILE* stream)
{
fputs(g_usage, stream);
return;
}
static double get_double(const char* name, const char* d_string, int* number_of_decimals)
{
char* end;
double d = strtod(d_string, &end);
if (d == 0 && end == d_string) {
fprintf(stderr, "%s: invalid argument \"%s\"\n", name, d_string);
print_usage(stderr);
exit(1);
}
if (char* dot = strchr(d_string, '.'))
*number_of_decimals = strlen(dot + 1);
else
*number_of_decimals = 0;
return d;
}
int main(int argc, const char* argv[])
{
if (pledge("stdio", nullptr) < 0) {
perror("pledge");
return 1;
}
if (unveil(nullptr, nullptr) < 0) {
perror("unveil");
return 1;
}
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
print_usage(stdout);
exit(0);
}
}
double start = 1, step = 1, end = 1;
int number_of_start_decimals = 0, number_of_step_decimals = 0, number_of_end_decimals = 0;
switch (argc) {
case 2:
end = get_double(argv[0], argv[1], &number_of_end_decimals);
break;
case 3:
start = get_double(argv[0], argv[1], &number_of_start_decimals);
end = get_double(argv[0], argv[2], &number_of_end_decimals);
break;
case 4:
start = get_double(argv[0], argv[1], &number_of_start_decimals);
step = get_double(argv[0], argv[2], &number_of_step_decimals);
end = get_double(argv[0], argv[3], &number_of_end_decimals);
break;
default:
fprintf(stderr, "%s: unexpected number of arguments\n", argv[0]);
print_usage(stderr);
return 1;
}
if (step == 0) {
fprintf(stderr, "%s: increment must not be 0\n", argv[0]);
return 1;
}
if (__builtin_isnan(start) || __builtin_isnan(step) || __builtin_isnan(end)) {
fprintf(stderr, "%s: start, step, and end must not be NaN\n", argv[0]);
return 1;
}
int number_of_decimals = max(number_of_start_decimals, max(number_of_step_decimals, number_of_end_decimals));
int n = (end - start) / step;
double d = start;
for (int i = 0; i <= n; ++i) {
char buf[40];
snprintf(buf, sizeof(buf), "%f", d);
if (char* dot = strchr(buf, '.')) {
if (number_of_decimals == 0)
*dot = '\0';
else if ((dot - buf) + 1 + number_of_decimals < (int)sizeof(buf))
dot[1 + number_of_decimals] = '\0';
}
printf("%s\n", buf);
d += step;
}
return 0;
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
bool now = false;
Core::ArgsParser args_parser;
args_parser.add_option(now, "Shut down now", "now", 'n');
args_parser.parse(argc, argv);
if (now) {
if (halt() < 0) {
perror("shutdown");
return 1;
}
} else {
args_parser.print_usage(stderr, argv[0]);
return 1;
}
}

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <LibCore/ArgsParser.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static volatile bool g_interrupted;
static void handle_sigint(int)
{
g_interrupted = true;
}
int main(int argc, char** argv)
{
int secs;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(secs, "Number of seconds to sleep for", "num-seconds");
args_parser.parse(argc, argv);
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, nullptr);
if (pledge("stdio sigaction", nullptr) < 0) {
perror("pledge");
return 1;
}
unsigned remaining = sleep(secs);
if (remaining) {
printf("Sleep interrupted with %u seconds remaining.\n", remaining);
}
signal(SIGINT, SIG_DFL);
if (g_interrupted)
raise(SIGINT);
return 0;
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/QuickSort.h>
#include <AK/String.h>
#include <AK/Vector.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{
if (pledge("stdio", nullptr) > 0) {
perror("pledge");
return 1;
}
Vector<String> lines;
for (;;) {
char* buffer = nullptr;
ssize_t buflen = 0;
size_t n;
errno = 0;
buflen = getline(&buffer, &n, stdin);
if (buflen == -1 && errno != 0) {
perror("getline");
exit(1);
}
if (buflen == -1)
break;
lines.append(buffer);
}
quick_sort(lines, [](auto& a, auto& b) {
return strcmp(a.characters(), b.characters()) < 0;
});
for (auto& line : lines) {
fputs(line.characters(), stdout);
}
return 0;
}

134
Userland/Utilities/stat.cpp Normal file
View file

@ -0,0 +1,134 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2020, Shannon Booth <shannon.ml.booth@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/String.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/DateTime.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
static int stat(const char* file, bool should_follow_links)
{
struct stat st;
int rc = should_follow_links ? stat(file, &st) : lstat(file, &st);
if (rc < 0) {
perror("lstat");
return 1;
}
printf(" File: %s\n", file);
printf(" Inode: %u\n", st.st_ino);
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))
printf(" Device: %u,%u\n", major(st.st_rdev), minor(st.st_rdev));
else
printf(" Size: %zd\n", st.st_size);
printf(" Links: %u\n", st.st_nlink);
printf(" Blocks: %u\n", st.st_blocks);
printf(" UID: %u", st.st_uid);
if (auto* pwd = getpwuid(st.st_uid)) {
printf(" (%s)", pwd->pw_name);
}
printf("\n");
printf(" GID: %u", st.st_gid);
if (auto* grp = getgrgid(st.st_gid)) {
printf(" (%s)", grp->gr_name);
}
printf("\n");
printf(" Mode: (%o/", st.st_mode);
if (S_ISDIR(st.st_mode))
printf("d");
else if (S_ISLNK(st.st_mode))
printf("l");
else if (S_ISBLK(st.st_mode))
printf("b");
else if (S_ISCHR(st.st_mode))
printf("c");
else if (S_ISFIFO(st.st_mode))
printf("f");
else if (S_ISSOCK(st.st_mode))
printf("s");
else if (S_ISREG(st.st_mode))
printf("-");
else
printf("?");
printf("%c%c%c%c%c%c%c%c",
st.st_mode & S_IRUSR ? 'r' : '-',
st.st_mode & S_IWUSR ? 'w' : '-',
st.st_mode & S_ISUID ? 's' : (st.st_mode & S_IXUSR ? 'x' : '-'),
st.st_mode & S_IRGRP ? 'r' : '-',
st.st_mode & S_IWGRP ? 'w' : '-',
st.st_mode & S_ISGID ? 's' : (st.st_mode & S_IXGRP ? 'x' : '-'),
st.st_mode & S_IROTH ? 'r' : '-',
st.st_mode & S_IWOTH ? 'w' : '-');
if (st.st_mode & S_ISVTX)
printf("t");
else
printf("%c", st.st_mode & S_IXOTH ? 'x' : '-');
printf(")\n");
auto print_time = [](time_t t) {
printf("%s\n", Core::DateTime::from_timestamp(t).to_string().characters());
};
printf("Accessed: ");
print_time(st.st_atime);
printf("Modified: ");
print_time(st.st_mtime);
printf(" Changed: ");
print_time(st.st_ctime);
return 0;
}
int main(int argc, char** argv)
{
if (pledge("stdio rpath", nullptr) < 0) {
perror("pledge");
return 1;
}
bool should_follow_links = false;
Vector<const char*> files;
auto args_parser = Core::ArgsParser();
args_parser.add_option(should_follow_links, "Follow links to files", nullptr, 'L');
args_parser.add_positional_argument(files, "File(s) to stat", "file", Core::ArgsParser::Required::Yes);
args_parser.parse(argc, argv);
int ret = 0;
for (auto& file : files)
ret |= stat(file, should_follow_links);
return ret;
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Assertions.h>
#include <AK/LogStream.h>
#include <AK/Types.h>
#include <Kernel/API/Syscall.h>
#include <LibC/sys/arch/i386/regs.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <unistd.h>
static int g_pid = -1;
static void handle_sigint(int)
{
if (g_pid == -1)
return;
if (ptrace(PT_DETACH, g_pid, 0, 0) == -1) {
perror("detach");
}
}
int main(int argc, char** argv)
{
if (pledge("stdio proc exec ptrace sigaction", nullptr) < 0) {
perror("pledge");
return 1;
}
Vector<const char*> child_argv;
const char* output_filename = nullptr;
auto trace_file = Core::File::standard_output();
Core::ArgsParser parser;
parser.set_general_help(
"Trace all syscalls and their result.");
parser.add_option(g_pid, "Trace the given PID", "pid", 'p', "pid");
parser.add_option(output_filename, "Filename to write output to", "output", 'o', "output");
parser.add_positional_argument(child_argv, "Arguments to exec", "argument", Core::ArgsParser::Required::No);
parser.parse(argc, argv);
if (output_filename != nullptr) {
auto open_result = Core::File::open(output_filename, Core::IODevice::OpenMode::WriteOnly);
if (open_result.is_error()) {
outln(stderr, "Failed to open output file: {}", open_result.error());
return 1;
}
trace_file = open_result.value();
}
int status;
if (g_pid == -1) {
if (child_argv.is_empty()) {
outln(stderr, "strace: Expected either a pid or some arguments\n");
return 1;
}
child_argv.append(nullptr);
int pid = fork();
if (pid < 0) {
perror("fork");
return 1;
}
if (!pid) {
if (ptrace(PT_TRACE_ME, 0, 0, 0) == -1) {
perror("traceme");
return 1;
}
int rc = execvp(child_argv.first(), const_cast<char**>(child_argv.data()));
if (rc < 0) {
perror("execvp");
exit(1);
}
ASSERT_NOT_REACHED();
}
g_pid = pid;
if (waitpid(pid, &status, WSTOPPED | WEXITED) != pid || !WIFSTOPPED(status)) {
perror("waitpid");
return 1;
}
}
struct sigaction sa;
memset(&sa, 0, sizeof(struct sigaction));
sa.sa_handler = handle_sigint;
sigaction(SIGINT, &sa, nullptr);
if (ptrace(PT_ATTACH, g_pid, 0, 0) == -1) {
perror("attach");
return 1;
}
if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
perror("waitpid");
return 1;
}
for (;;) {
if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
perror("syscall");
return 1;
}
if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
perror("wait_pid");
return 1;
}
PtraceRegisters regs = {};
if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
perror("getregs");
return 1;
}
u32 syscall_index = regs.eax;
u32 arg1 = regs.edx;
u32 arg2 = regs.ecx;
u32 arg3 = regs.ebx;
if (ptrace(PT_SYSCALL, g_pid, 0, 0) == -1) {
perror("syscall");
return 1;
}
if (waitpid(g_pid, &status, WSTOPPED | WEXITED) != g_pid || !WIFSTOPPED(status)) {
perror("wait_pid");
return 1;
}
if (ptrace(PT_GETREGS, g_pid, &regs, 0) == -1) {
perror("getregs");
return 1;
}
u32 res = regs.eax;
trace_file->printf("%s(0x%x, 0x%x, 0x%x)\t=%d\n",
Syscall::to_string(
(Syscall::Function)syscall_index),
arg1,
arg2,
arg3,
res);
}
return 0;
}

101
Userland/Utilities/su.cpp Normal file
View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <AK/Vector.h>
#include <LibCore/Account.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/GetPassword.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
extern "C" int main(int, char**);
int main(int argc, char** argv)
{
if (pledge("stdio rpath tty exec id", nullptr) < 0) {
perror("pledge");
return 1;
}
if (!isatty(STDIN_FILENO)) {
warnln("{}: standard in is not a terminal", argv[0]);
return 1;
}
const char* user = nullptr;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(user, "User to switch to (defaults to user with UID 0)", "user", Core::ArgsParser::Required::No);
args_parser.parse(argc, argv);
if (geteuid() != 0) {
warnln("Not running as root :(");
return 1;
}
auto account_or_error = (user)
? Core::Account::from_name(user, Core::Account::OpenPasswdFile::No, Core::Account::OpenShadowFile::ReadOnly)
: Core::Account::from_uid(0, Core::Account::OpenPasswdFile::No, Core::Account::OpenShadowFile::ReadOnly);
if (account_or_error.is_error()) {
fprintf(stderr, "Core::Account::from_name: %s\n", account_or_error.error().characters());
return 1;
}
if (pledge("stdio tty exec id", nullptr) < 0) {
perror("pledge");
return 1;
}
const auto& account = account_or_error.value();
if (getuid() != 0 && account.has_password()) {
auto password = Core::get_password();
if (password.is_error()) {
warnln("{}", password.error());
return 1;
}
if (!account.authenticate(password.value().characters())) {
warnln("Incorrect or disabled password.");
return 1;
}
}
if (pledge("stdio exec id", nullptr) < 0) {
perror("pledge");
return 1;
}
if (!account.login()) {
perror("Core::Account::login");
return 1;
}
execl(account.shell().characters(), account.shell().characters(), nullptr);
perror("execl");
return 1;
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <unistd.h>
int main(int, char**)
{
sync();
return 0;
}

Some files were not shown because too many files have changed in this diff Show more