mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 01:27:43 +00:00
Services: Move to Userland/Services/
This commit is contained in:
parent
4055b03291
commit
c7ac7e6eaf
170 changed files with 4 additions and 4 deletions
6
Userland/Services/AudioServer/AudioClient.ipc
Normal file
6
Userland/Services/AudioServer/AudioClient.ipc
Normal file
|
@ -0,0 +1,6 @@
|
|||
endpoint AudioClient = 82
|
||||
{
|
||||
FinishedPlayingBuffer(i32 buffer_id) =|
|
||||
MutedStateChanged(bool muted) =|
|
||||
MainMixVolumeChanged(i32 volume) =|
|
||||
}
|
21
Userland/Services/AudioServer/AudioServer.ipc
Normal file
21
Userland/Services/AudioServer/AudioServer.ipc
Normal file
|
@ -0,0 +1,21 @@
|
|||
endpoint AudioServer = 85
|
||||
{
|
||||
// Basic protocol
|
||||
Greet() => (i32 client_id)
|
||||
|
||||
// Mixer functions
|
||||
SetMuted(bool muted) => ()
|
||||
GetMuted() => (bool muted)
|
||||
GetMainMixVolume() => (i32 volume)
|
||||
SetMainMixVolume(i32 volume) => ()
|
||||
|
||||
// Buffer playback
|
||||
EnqueueBuffer(i32 buffer_id, int sample_count) => (bool success)
|
||||
SetPaused(bool paused) => ()
|
||||
ClearBuffer(bool paused) => ()
|
||||
|
||||
//Buffer information
|
||||
GetRemainingSamples() => (int remaining_samples)
|
||||
GetPlayedSamples() => (int played_samples)
|
||||
GetPlayingBuffer() => (i32 buffer_id)
|
||||
}
|
13
Userland/Services/AudioServer/CMakeLists.txt
Normal file
13
Userland/Services/AudioServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
compile_ipc(AudioServer.ipc AudioServerEndpoint.h)
|
||||
compile_ipc(AudioClient.ipc AudioClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
Mixer.cpp
|
||||
main.cpp
|
||||
AudioServerEndpoint.h
|
||||
AudioClientEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(AudioServer)
|
||||
target_link_libraries(AudioServer LibCore LibThread LibIPC)
|
168
Userland/Services/AudioServer/ClientConnection.cpp
Normal file
168
Userland/Services/AudioServer/ClientConnection.cpp
Normal 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 "ClientConnection.h"
|
||||
#include "Mixer.h"
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <AudioServer/AudioClientEndpoint.h>
|
||||
#include <LibAudio/Buffer.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace AudioServer {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
|
||||
void ClientConnection::for_each(Function<void(ClientConnection&)> callback)
|
||||
{
|
||||
NonnullRefPtrVector<ClientConnection> connections;
|
||||
for (auto& it : s_connections)
|
||||
connections.append(*it.value);
|
||||
for (auto& connection : connections)
|
||||
callback(connection);
|
||||
}
|
||||
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id, Mixer& mixer)
|
||||
: IPC::ClientConnection<AudioClientEndpoint, AudioServerEndpoint>(*this, move(client_socket), client_id)
|
||||
, m_mixer(mixer)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
void ClientConnection::did_finish_playing_buffer(Badge<BufferQueue>, int buffer_id)
|
||||
{
|
||||
post_message(Messages::AudioClient::FinishedPlayingBuffer(buffer_id));
|
||||
}
|
||||
|
||||
void ClientConnection::did_change_muted_state(Badge<Mixer>, bool muted)
|
||||
{
|
||||
post_message(Messages::AudioClient::MutedStateChanged(muted));
|
||||
}
|
||||
|
||||
void ClientConnection::did_change_main_mix_volume(Badge<Mixer>, int volume)
|
||||
{
|
||||
post_message(Messages::AudioClient::MainMixVolumeChanged(volume));
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GreetResponse> ClientConnection::handle(const Messages::AudioServer::Greet&)
|
||||
{
|
||||
return make<Messages::AudioServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> ClientConnection::handle(const Messages::AudioServer::GetMainMixVolume&)
|
||||
{
|
||||
return make<Messages::AudioServer::GetMainMixVolumeResponse>(m_mixer.main_volume());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> ClientConnection::handle(const Messages::AudioServer::SetMainMixVolume& message)
|
||||
{
|
||||
m_mixer.set_main_volume(message.volume());
|
||||
return make<Messages::AudioServer::SetMainMixVolumeResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::EnqueueBufferResponse> ClientConnection::handle(const Messages::AudioServer::EnqueueBuffer& message)
|
||||
{
|
||||
auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.buffer_id());
|
||||
if (!shared_buffer) {
|
||||
// FIXME: The shared buffer should have been retrieved for us already.
|
||||
// We don't want to do IPC error checking at this layer.
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!m_queue)
|
||||
m_queue = m_mixer.create_queue(*this);
|
||||
|
||||
if (m_queue->is_full())
|
||||
return make<Messages::AudioServer::EnqueueBufferResponse>(false);
|
||||
|
||||
m_queue->enqueue(Audio::Buffer::create_with_shared_buffer(*shared_buffer, message.sample_count()));
|
||||
return make<Messages::AudioServer::EnqueueBufferResponse>(true);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> ClientConnection::handle(const Messages::AudioServer::GetRemainingSamples&)
|
||||
{
|
||||
int remaining = 0;
|
||||
if (m_queue)
|
||||
remaining = m_queue->get_remaining_samples();
|
||||
|
||||
return make<Messages::AudioServer::GetRemainingSamplesResponse>(remaining);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> ClientConnection::handle(const Messages::AudioServer::GetPlayedSamples&)
|
||||
{
|
||||
int played = 0;
|
||||
if (m_queue)
|
||||
played = m_queue->get_played_samples();
|
||||
|
||||
return make<Messages::AudioServer::GetPlayedSamplesResponse>(played);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetPausedResponse> ClientConnection::handle(const Messages::AudioServer::SetPaused& message)
|
||||
{
|
||||
if (m_queue)
|
||||
m_queue->set_paused(message.paused());
|
||||
return make<Messages::AudioServer::SetPausedResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::ClearBufferResponse> ClientConnection::handle(const Messages::AudioServer::ClearBuffer& message)
|
||||
{
|
||||
if (m_queue)
|
||||
m_queue->clear(message.paused());
|
||||
return make<Messages::AudioServer::ClearBufferResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> ClientConnection::handle(const Messages::AudioServer::GetPlayingBuffer&)
|
||||
{
|
||||
int id = -1;
|
||||
if (m_queue)
|
||||
id = m_queue->get_playing_buffer();
|
||||
return make<Messages::AudioServer::GetPlayingBufferResponse>(id);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::GetMutedResponse> ClientConnection::handle(const Messages::AudioServer::GetMuted&)
|
||||
{
|
||||
return make<Messages::AudioServer::GetMutedResponse>(m_mixer.is_muted());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::AudioServer::SetMutedResponse> ClientConnection::handle(const Messages::AudioServer::SetMuted& message)
|
||||
{
|
||||
m_mixer.set_muted(message.muted());
|
||||
return make<Messages::AudioServer::SetMutedResponse>();
|
||||
}
|
||||
}
|
75
Userland/Services/AudioServer/ClientConnection.h
Normal file
75
Userland/Services/AudioServer/ClientConnection.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AudioServer/AudioClientEndpoint.h>
|
||||
#include <AudioServer/AudioServerEndpoint.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
namespace Audio {
|
||||
class Buffer;
|
||||
}
|
||||
|
||||
namespace AudioServer {
|
||||
|
||||
class BufferQueue;
|
||||
class Mixer;
|
||||
|
||||
class ClientConnection final : public IPC::ClientConnection<AudioClientEndpoint, AudioServerEndpoint>
|
||||
, public AudioServerEndpoint {
|
||||
C_OBJECT(ClientConnection)
|
||||
public:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id, Mixer& mixer);
|
||||
~ClientConnection() override;
|
||||
|
||||
void did_finish_playing_buffer(Badge<BufferQueue>, int buffer_id);
|
||||
void did_change_muted_state(Badge<Mixer>, bool muted);
|
||||
void did_change_main_mix_volume(Badge<Mixer>, int volume);
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
static void for_each(Function<void(ClientConnection&)>);
|
||||
|
||||
private:
|
||||
virtual OwnPtr<Messages::AudioServer::GreetResponse> handle(const Messages::AudioServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetMainMixVolumeResponse> handle(const Messages::AudioServer::GetMainMixVolume&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetMainMixVolumeResponse> handle(const Messages::AudioServer::SetMainMixVolume&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::EnqueueBufferResponse> handle(const Messages::AudioServer::EnqueueBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetRemainingSamplesResponse> handle(const Messages::AudioServer::GetRemainingSamples&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetPlayedSamplesResponse> handle(const Messages::AudioServer::GetPlayedSamples&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetPausedResponse> handle(const Messages::AudioServer::SetPaused&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::ClearBufferResponse> handle(const Messages::AudioServer::ClearBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetPlayingBufferResponse> handle(const Messages::AudioServer::GetPlayingBuffer&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::GetMutedResponse> handle(const Messages::AudioServer::GetMuted&) override;
|
||||
virtual OwnPtr<Messages::AudioServer::SetMutedResponse> handle(const Messages::AudioServer::SetMuted&) override;
|
||||
|
||||
Mixer& m_mixer;
|
||||
RefPtr<BufferQueue> m_queue;
|
||||
};
|
||||
|
||||
}
|
166
Userland/Services/AudioServer/Mixer.cpp
Normal file
166
Userland/Services/AudioServer/Mixer.cpp
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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/Array.h>
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/NumericLimits.h>
|
||||
#include <AudioServer/ClientConnection.h>
|
||||
#include <AudioServer/Mixer.h>
|
||||
#include <pthread.h>
|
||||
#include <strings.h>
|
||||
|
||||
namespace AudioServer {
|
||||
|
||||
Mixer::Mixer()
|
||||
: m_device(Core::File::construct("/dev/audio", this))
|
||||
, m_sound_thread(LibThread::Thread::construct(
|
||||
[this] {
|
||||
mix();
|
||||
return 0;
|
||||
},
|
||||
"AudioServer[mixer]"))
|
||||
{
|
||||
if (!m_device->open(Core::IODevice::WriteOnly)) {
|
||||
dbgln("Can't open audio device: {}", m_device->error_string());
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&m_pending_mutex, nullptr);
|
||||
pthread_cond_init(&m_pending_cond, nullptr);
|
||||
|
||||
m_zero_filled_buffer = (u8*)malloc(4096);
|
||||
bzero(m_zero_filled_buffer, 4096);
|
||||
m_sound_thread->start();
|
||||
}
|
||||
|
||||
Mixer::~Mixer()
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtr<BufferQueue> Mixer::create_queue(ClientConnection& client)
|
||||
{
|
||||
auto queue = adopt(*new BufferQueue(client));
|
||||
pthread_mutex_lock(&m_pending_mutex);
|
||||
m_pending_mixing.append(*queue);
|
||||
m_added_queue = true;
|
||||
pthread_cond_signal(&m_pending_cond);
|
||||
pthread_mutex_unlock(&m_pending_mutex);
|
||||
return queue;
|
||||
}
|
||||
|
||||
void Mixer::mix()
|
||||
{
|
||||
decltype(m_pending_mixing) active_mix_queues;
|
||||
|
||||
for (;;) {
|
||||
if (active_mix_queues.is_empty() || m_added_queue) {
|
||||
pthread_mutex_lock(&m_pending_mutex);
|
||||
pthread_cond_wait(&m_pending_cond, &m_pending_mutex);
|
||||
active_mix_queues.append(move(m_pending_mixing));
|
||||
pthread_mutex_unlock(&m_pending_mutex);
|
||||
m_added_queue = false;
|
||||
}
|
||||
|
||||
active_mix_queues.remove_all_matching([&](auto& entry) { return !entry->client(); });
|
||||
|
||||
Audio::Sample mixed_buffer[1024];
|
||||
auto mixed_buffer_length = (int)(sizeof(mixed_buffer) / sizeof(Audio::Sample));
|
||||
|
||||
// Mix the buffers together into the output
|
||||
for (auto& queue : active_mix_queues) {
|
||||
if (!queue->client()) {
|
||||
queue->clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < mixed_buffer_length; ++i) {
|
||||
auto& mixed_sample = mixed_buffer[i];
|
||||
Audio::Sample sample;
|
||||
if (!queue->get_next_sample(sample))
|
||||
break;
|
||||
mixed_sample += sample;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_muted) {
|
||||
m_device->write(m_zero_filled_buffer, 4096);
|
||||
} else {
|
||||
Array<u8, 4096> buffer;
|
||||
OutputMemoryStream stream { buffer };
|
||||
|
||||
for (int i = 0; i < mixed_buffer_length; ++i) {
|
||||
auto& mixed_sample = mixed_buffer[i];
|
||||
|
||||
mixed_sample.scale(m_main_volume);
|
||||
mixed_sample.clip();
|
||||
|
||||
LittleEndian<i16> out_sample;
|
||||
out_sample = mixed_sample.left * NumericLimits<i16>::max();
|
||||
stream << out_sample;
|
||||
|
||||
out_sample = mixed_sample.right * NumericLimits<i16>::max();
|
||||
stream << out_sample;
|
||||
}
|
||||
|
||||
ASSERT(stream.is_end());
|
||||
ASSERT(!stream.has_any_error());
|
||||
m_device->write(stream.data(), stream.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mixer::set_main_volume(int volume)
|
||||
{
|
||||
if (volume > 100)
|
||||
m_main_volume = 100;
|
||||
else
|
||||
m_main_volume = volume;
|
||||
ClientConnection::for_each([volume](ClientConnection& client) {
|
||||
client.did_change_main_mix_volume({}, volume);
|
||||
});
|
||||
}
|
||||
|
||||
void Mixer::set_muted(bool muted)
|
||||
{
|
||||
if (m_muted == muted)
|
||||
return;
|
||||
m_muted = muted;
|
||||
ClientConnection::for_each([muted](ClientConnection& client) {
|
||||
client.did_change_muted_state({}, muted);
|
||||
});
|
||||
}
|
||||
|
||||
BufferQueue::BufferQueue(ClientConnection& client)
|
||||
: m_client(client)
|
||||
{
|
||||
}
|
||||
|
||||
void BufferQueue::enqueue(NonnullRefPtr<Audio::Buffer>&& buffer)
|
||||
{
|
||||
m_remaining_samples += buffer->sample_count();
|
||||
m_queue.enqueue(move(buffer));
|
||||
}
|
||||
}
|
144
Userland/Services/AudioServer/Mixer.h
Normal file
144
Userland/Services/AudioServer/Mixer.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ClientConnection.h"
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/Queue.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/WeakPtr.h>
|
||||
#include <LibAudio/Buffer.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibThread/Lock.h>
|
||||
#include <LibThread/Thread.h>
|
||||
|
||||
namespace AudioServer {
|
||||
|
||||
class ClientConnection;
|
||||
|
||||
class BufferQueue : public RefCounted<BufferQueue> {
|
||||
public:
|
||||
explicit BufferQueue(ClientConnection&);
|
||||
~BufferQueue() { }
|
||||
|
||||
bool is_full() const { return m_queue.size() >= 3; }
|
||||
void enqueue(NonnullRefPtr<Audio::Buffer>&&);
|
||||
|
||||
bool get_next_sample(Audio::Sample& sample)
|
||||
{
|
||||
if (m_paused)
|
||||
return false;
|
||||
|
||||
while (!m_current && !m_queue.is_empty())
|
||||
m_current = m_queue.dequeue();
|
||||
|
||||
if (!m_current)
|
||||
return false;
|
||||
|
||||
sample = m_current->samples()[m_position++];
|
||||
--m_remaining_samples;
|
||||
++m_played_samples;
|
||||
|
||||
if (m_position >= m_current->sample_count()) {
|
||||
m_client->did_finish_playing_buffer({}, m_current->shbuf_id());
|
||||
m_current = nullptr;
|
||||
m_position = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ClientConnection* client() { return m_client.ptr(); }
|
||||
|
||||
void clear(bool paused = false)
|
||||
{
|
||||
m_queue.clear();
|
||||
m_position = 0;
|
||||
m_remaining_samples = 0;
|
||||
m_played_samples = 0;
|
||||
m_current = nullptr;
|
||||
m_paused = paused;
|
||||
}
|
||||
|
||||
void set_paused(bool paused)
|
||||
{
|
||||
m_paused = paused;
|
||||
}
|
||||
|
||||
int get_remaining_samples() const { return m_remaining_samples; }
|
||||
int get_played_samples() const { return m_played_samples; }
|
||||
int get_playing_buffer() const
|
||||
{
|
||||
if (m_current)
|
||||
return m_current->shbuf_id();
|
||||
return -1;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<Audio::Buffer> m_current;
|
||||
Queue<NonnullRefPtr<Audio::Buffer>> m_queue;
|
||||
int m_position { 0 };
|
||||
int m_remaining_samples { 0 };
|
||||
int m_played_samples { 0 };
|
||||
bool m_paused { false };
|
||||
WeakPtr<ClientConnection> m_client;
|
||||
};
|
||||
|
||||
class Mixer : public Core::Object {
|
||||
C_OBJECT(Mixer)
|
||||
public:
|
||||
Mixer();
|
||||
virtual ~Mixer() override;
|
||||
|
||||
NonnullRefPtr<BufferQueue> create_queue(ClientConnection&);
|
||||
|
||||
int main_volume() const { return m_main_volume; }
|
||||
void set_main_volume(int volume);
|
||||
|
||||
bool is_muted() const { return m_muted; }
|
||||
void set_muted(bool);
|
||||
|
||||
private:
|
||||
Vector<NonnullRefPtr<BufferQueue>> m_pending_mixing;
|
||||
Atomic<bool> m_added_queue { false };
|
||||
pthread_mutex_t m_pending_mutex;
|
||||
pthread_cond_t m_pending_cond;
|
||||
|
||||
RefPtr<Core::File> m_device;
|
||||
|
||||
NonnullRefPtr<LibThread::Thread> m_sound_thread;
|
||||
|
||||
bool m_muted { false };
|
||||
int m_main_volume { 100 };
|
||||
|
||||
u8* m_zero_filled_buffer { nullptr };
|
||||
|
||||
void mix();
|
||||
};
|
||||
}
|
63
Userland/Services/AudioServer/main.cpp
Normal file
63
Userland/Services/AudioServer/main.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 "Mixer.h"
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
if (pledge("stdio thread shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
AudioServer::Mixer mixer;
|
||||
|
||||
auto server = Core::LocalServer::construct();
|
||||
bool ok = server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
server->on_ready_to_accept = [&] {
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
dbgln("AudioServer: accept failed.");
|
||||
return;
|
||||
}
|
||||
static int s_next_client_id = 0;
|
||||
int client_id = ++s_next_client_id;
|
||||
IPC::new_client_connection<AudioServer::ClientConnection>(client_socket.release_nonnull(), client_id, mixer);
|
||||
};
|
||||
|
||||
if (pledge("stdio thread shared_buffer accept", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
18
Userland/Services/CMakeLists.txt
Normal file
18
Userland/Services/CMakeLists.txt
Normal file
|
@ -0,0 +1,18 @@
|
|||
add_subdirectory(AudioServer)
|
||||
add_subdirectory(ChessEngine)
|
||||
add_subdirectory(Clipboard)
|
||||
add_subdirectory(CrashDaemon)
|
||||
add_subdirectory(DHCPClient)
|
||||
add_subdirectory(EchoServer)
|
||||
add_subdirectory(ImageDecoder)
|
||||
add_subdirectory(LaunchServer)
|
||||
add_subdirectory(LookupServer)
|
||||
add_subdirectory(NotificationServer)
|
||||
add_subdirectory(ProtocolServer)
|
||||
add_subdirectory(SystemMenu)
|
||||
add_subdirectory(SystemServer)
|
||||
add_subdirectory(Taskbar)
|
||||
add_subdirectory(TelnetServer)
|
||||
add_subdirectory(WebContent)
|
||||
add_subdirectory(WebServer)
|
||||
add_subdirectory(WindowServer)
|
8
Userland/Services/ChessEngine/CMakeLists.txt
Normal file
8
Userland/Services/ChessEngine/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
set(SOURCES
|
||||
ChessEngine.cpp
|
||||
main.cpp
|
||||
MCTSTree.cpp
|
||||
)
|
||||
|
||||
serenity_bin(ChessEngine)
|
||||
target_link_libraries(ChessEngine LibChess LibCore)
|
76
Userland/Services/ChessEngine/ChessEngine.cpp
Normal file
76
Userland/Services/ChessEngine/ChessEngine.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 "ChessEngine.h"
|
||||
#include "MCTSTree.h"
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
|
||||
using namespace Chess::UCI;
|
||||
|
||||
void ChessEngine::handle_uci()
|
||||
{
|
||||
send_command(IdCommand(IdCommand::Type::Name, "ChessEngine"));
|
||||
send_command(IdCommand(IdCommand::Type::Author, "the SerenityOS developers"));
|
||||
send_command(UCIOkCommand());
|
||||
}
|
||||
|
||||
void ChessEngine::handle_position(const PositionCommand& command)
|
||||
{
|
||||
// FIXME: Implement fen board position.
|
||||
ASSERT(!command.fen().has_value());
|
||||
m_board = Chess::Board();
|
||||
for (auto& move : command.moves()) {
|
||||
ASSERT(m_board.apply_move(move));
|
||||
}
|
||||
}
|
||||
|
||||
void ChessEngine::handle_go(const GoCommand& command)
|
||||
{
|
||||
// FIXME: A better algorithm than naive mcts.
|
||||
// FIXME: Add different ways to terminate search.
|
||||
ASSERT(command.movetime.has_value());
|
||||
|
||||
srand(arc4random());
|
||||
|
||||
Core::ElapsedTimer elapsed_time;
|
||||
elapsed_time.start();
|
||||
|
||||
MCTSTree mcts(m_board);
|
||||
|
||||
// FIXME: optimize simulations enough for use.
|
||||
mcts.set_eval_method(MCTSTree::EvalMethod::Heuristic);
|
||||
|
||||
int rounds = 0;
|
||||
while (elapsed_time.elapsed() <= command.movetime.value()) {
|
||||
mcts.do_round();
|
||||
++rounds;
|
||||
}
|
||||
dbg() << "MCTS finished " << rounds << " rounds.";
|
||||
dbg() << "MCTS evaluation " << mcts.expected_value();
|
||||
auto best_move = mcts.best_move();
|
||||
dbg() << "MCTS best move " << best_move.to_long_algebraic();
|
||||
send_command(BestMoveCommand(best_move));
|
||||
}
|
49
Userland/Services/ChessEngine/ChessEngine.h
Normal file
49
Userland/Services/ChessEngine/ChessEngine.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibChess/Chess.h>
|
||||
#include <LibChess/UCIEndpoint.h>
|
||||
|
||||
class ChessEngine : public Chess::UCI::Endpoint {
|
||||
C_OBJECT(ChessEngine)
|
||||
public:
|
||||
virtual ~ChessEngine() override { }
|
||||
|
||||
ChessEngine() { }
|
||||
ChessEngine(NonnullRefPtr<Core::IODevice> in, NonnullRefPtr<Core::IODevice> out)
|
||||
: Endpoint(in, out)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void handle_uci();
|
||||
virtual void handle_position(const Chess::UCI::PositionCommand&);
|
||||
virtual void handle_go(const Chess::UCI::GoCommand&);
|
||||
|
||||
private:
|
||||
Chess::Board m_board;
|
||||
};
|
180
Userland/Services/ChessEngine/MCTSTree.cpp
Normal file
180
Userland/Services/ChessEngine/MCTSTree.cpp
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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 "MCTSTree.h"
|
||||
#include <AK/String.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
MCTSTree::MCTSTree(const Chess::Board& board, double exploration_parameter, MCTSTree* parent)
|
||||
: m_parent(parent)
|
||||
, m_exploration_parameter(exploration_parameter)
|
||||
, m_board(board)
|
||||
{
|
||||
if (m_parent)
|
||||
m_eval_method = m_parent->eval_method();
|
||||
}
|
||||
|
||||
MCTSTree& MCTSTree::select_leaf()
|
||||
{
|
||||
if (!expanded() || m_children.size() == 0)
|
||||
return *this;
|
||||
|
||||
MCTSTree* node = nullptr;
|
||||
double max_uct = -double(INFINITY);
|
||||
for (auto& child : m_children) {
|
||||
double uct = child.uct(m_board.turn());
|
||||
if (uct >= max_uct) {
|
||||
max_uct = uct;
|
||||
node = &child;
|
||||
}
|
||||
}
|
||||
ASSERT(node);
|
||||
return node->select_leaf();
|
||||
}
|
||||
|
||||
MCTSTree& MCTSTree::expand()
|
||||
{
|
||||
ASSERT(!expanded() || m_children.size() == 0);
|
||||
|
||||
if (!m_moves_generated) {
|
||||
m_board.generate_moves([&](Chess::Move move) {
|
||||
Chess::Board clone = m_board;
|
||||
clone.apply_move(move);
|
||||
m_children.append(make<MCTSTree>(clone, m_exploration_parameter, this));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
m_moves_generated = true;
|
||||
}
|
||||
|
||||
if (m_children.size() == 0) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
for (auto& child : m_children) {
|
||||
if (child.m_simulations == 0) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
int MCTSTree::simulate_game() const
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
Chess::Board clone = m_board;
|
||||
while (!clone.game_finished()) {
|
||||
clone.apply_move(clone.random_move());
|
||||
}
|
||||
return clone.game_score();
|
||||
}
|
||||
|
||||
int MCTSTree::heuristic() const
|
||||
{
|
||||
if (m_board.game_finished())
|
||||
return m_board.game_score();
|
||||
|
||||
double winchance = max(min(double(m_board.material_imbalance()) / 6, 1.0), -1.0);
|
||||
|
||||
double random = double(rand()) / RAND_MAX;
|
||||
if (winchance >= random)
|
||||
return 1;
|
||||
if (winchance <= -random)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MCTSTree::apply_result(int game_score)
|
||||
{
|
||||
m_simulations++;
|
||||
m_white_points += game_score;
|
||||
|
||||
if (m_parent)
|
||||
m_parent->apply_result(game_score);
|
||||
}
|
||||
|
||||
void MCTSTree::do_round()
|
||||
{
|
||||
auto& node = select_leaf().expand();
|
||||
|
||||
int result;
|
||||
if (m_eval_method == EvalMethod::Simulation) {
|
||||
result = node.simulate_game();
|
||||
} else {
|
||||
result = node.heuristic();
|
||||
}
|
||||
node.apply_result(result);
|
||||
}
|
||||
|
||||
Chess::Move MCTSTree::best_move() const
|
||||
{
|
||||
int score_multiplier = (m_board.turn() == Chess::Color::White) ? 1 : -1;
|
||||
|
||||
Chess::Move best_move = { { 0, 0 }, { 0, 0 } };
|
||||
double best_score = -double(INFINITY);
|
||||
ASSERT(m_children.size());
|
||||
for (auto& node : m_children) {
|
||||
double node_score = node.expected_value() * score_multiplier;
|
||||
if (node_score >= best_score) {
|
||||
// The best move is the last move made in the child.
|
||||
best_move = node.m_board.moves()[node.m_board.moves().size() - 1];
|
||||
best_score = node_score;
|
||||
}
|
||||
}
|
||||
|
||||
return best_move;
|
||||
}
|
||||
|
||||
double MCTSTree::expected_value() const
|
||||
{
|
||||
if (m_simulations == 0)
|
||||
return 0;
|
||||
|
||||
return double(m_white_points) / m_simulations;
|
||||
}
|
||||
|
||||
double MCTSTree::uct(Chess::Color color) const
|
||||
{
|
||||
// UCT: Upper Confidence Bound Applied to Trees.
|
||||
// Kocsis, Levente; Szepesvári, Csaba (2006). "Bandit based Monte-Carlo Planning"
|
||||
|
||||
// Fun fact: Szepesvári was my data structures professor.
|
||||
double expected = expected_value() * ((color == Chess::Color::White) ? 1 : -1);
|
||||
return expected + m_exploration_parameter * sqrt(log(m_parent->m_simulations) / m_simulations);
|
||||
}
|
||||
|
||||
bool MCTSTree::expanded() const
|
||||
{
|
||||
if (!m_moves_generated)
|
||||
return false;
|
||||
|
||||
for (auto& child : m_children) {
|
||||
if (child.m_simulations == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
67
Userland/Services/ChessEngine/MCTSTree.h
Normal file
67
Userland/Services/ChessEngine/MCTSTree.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <LibChess/Chess.h>
|
||||
#include <math.h>
|
||||
|
||||
class MCTSTree {
|
||||
public:
|
||||
enum EvalMethod {
|
||||
Simulation,
|
||||
Heuristic,
|
||||
};
|
||||
|
||||
MCTSTree(const Chess::Board& board, double exploration_parameter = sqrt(2), MCTSTree* parent = nullptr);
|
||||
|
||||
MCTSTree& select_leaf();
|
||||
MCTSTree& expand();
|
||||
int simulate_game() const;
|
||||
int heuristic() const;
|
||||
void apply_result(int game_score);
|
||||
void do_round();
|
||||
|
||||
Chess::Move best_move() const;
|
||||
double expected_value() const;
|
||||
double uct(Chess::Color color) const;
|
||||
bool expanded() const;
|
||||
|
||||
EvalMethod eval_method() const { return m_eval_method; }
|
||||
void set_eval_method(EvalMethod method) { m_eval_method = method; }
|
||||
|
||||
private:
|
||||
NonnullOwnPtrVector<MCTSTree> m_children;
|
||||
MCTSTree* m_parent { nullptr };
|
||||
int m_white_points { 0 };
|
||||
int m_simulations { 0 };
|
||||
bool m_moves_generated { false };
|
||||
double m_exploration_parameter;
|
||||
EvalMethod m_eval_method { EvalMethod::Simulation };
|
||||
Chess::Board m_board;
|
||||
};
|
49
Userland/Services/ChessEngine/main.cpp
Normal file
49
Userland/Services/ChessEngine/main.cpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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 "ChessEngine.h"
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/File.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
if (pledge("stdio shared_buffer accept unix rpath cpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
Core::EventLoop loop;
|
||||
if (pledge("stdio shared_buffer unix", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto engine = ChessEngine::construct(Core::File::standard_input(), Core::File::standard_output());
|
||||
return loop.exec();
|
||||
}
|
13
Userland/Services/Clipboard/CMakeLists.txt
Normal file
13
Userland/Services/Clipboard/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
compile_ipc(ClipboardServer.ipc ClipboardServerEndpoint.h)
|
||||
compile_ipc(ClipboardClient.ipc ClipboardClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
ClipboardClientEndpoint.h
|
||||
ClipboardServerEndpoint.h
|
||||
Storage.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(Clipboard)
|
||||
target_link_libraries(Clipboard LibCore LibIPC)
|
103
Userland/Services/Clipboard/ClientConnection.cpp
Normal file
103
Userland/Services/Clipboard/ClientConnection.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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/Badge.h>
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <Clipboard/ClientConnection.h>
|
||||
#include <Clipboard/ClipboardClientEndpoint.h>
|
||||
#include <Clipboard/Storage.h>
|
||||
|
||||
namespace Clipboard {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
|
||||
void ClientConnection::for_each_client(Function<void(ClientConnection&)> callback)
|
||||
{
|
||||
for (auto& it : s_connections) {
|
||||
callback(*it.value);
|
||||
}
|
||||
}
|
||||
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id)
|
||||
: IPC::ClientConnection<ClipboardClientEndpoint, ClipboardServerEndpoint>(*this, move(socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ClipboardServer::GreetResponse> ClientConnection::handle(const Messages::ClipboardServer::Greet&)
|
||||
{
|
||||
return make<Messages::ClipboardServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ClipboardServer::SetClipboardDataResponse> ClientConnection::handle(const Messages::ClipboardServer::SetClipboardData& message)
|
||||
{
|
||||
auto shared_buffer = SharedBuffer::create_from_shbuf_id(message.shbuf_id());
|
||||
if (!shared_buffer) {
|
||||
did_misbehave("SetClipboardData: Bad shared buffer ID");
|
||||
return {};
|
||||
}
|
||||
Storage::the().set_data(*shared_buffer, message.data_size(), message.mime_type(), message.metadata().entries());
|
||||
return make<Messages::ClipboardServer::SetClipboardDataResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ClipboardServer::GetClipboardDataResponse> ClientConnection::handle(const Messages::ClipboardServer::GetClipboardData&)
|
||||
{
|
||||
auto& storage = Storage::the();
|
||||
|
||||
i32 shbuf_id = -1;
|
||||
if (storage.data_size()) {
|
||||
// FIXME: Optimize case where an app is copy/pasting within itself.
|
||||
// We can just reuse the SharedBuffer then, since it will have the same peer PID.
|
||||
// It would be even nicer if a SharedBuffer could have an arbitrary number of clients..
|
||||
RefPtr<SharedBuffer> shared_buffer = SharedBuffer::create_with_size(storage.data_size());
|
||||
ASSERT(shared_buffer);
|
||||
memcpy(shared_buffer->data<void>(), storage.data(), storage.data_size());
|
||||
shared_buffer->seal();
|
||||
shared_buffer->share_with(client_pid());
|
||||
shbuf_id = shared_buffer->shbuf_id();
|
||||
|
||||
// FIXME: This is a workaround for the fact that SharedBuffers will go away if neither side is retaining them.
|
||||
// After we respond to GetClipboardData, we have to wait for the client to ref the buffer on his side.
|
||||
m_last_sent_buffer = move(shared_buffer);
|
||||
}
|
||||
return make<Messages::ClipboardServer::GetClipboardDataResponse>(shbuf_id, storage.data_size(), storage.mime_type(), storage.metadata());
|
||||
}
|
||||
|
||||
void ClientConnection::notify_about_clipboard_change()
|
||||
{
|
||||
post_message(Messages::ClipboardClient::ClipboardDataChanged(Storage::the().mime_type()));
|
||||
}
|
||||
|
||||
}
|
60
Userland/Services/Clipboard/ClientConnection.h
Normal file
60
Userland/Services/Clipboard/ClientConnection.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <Clipboard/ClipboardClientEndpoint.h>
|
||||
#include <Clipboard/ClipboardServerEndpoint.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
namespace Clipboard {
|
||||
|
||||
class ClientConnection final
|
||||
: public IPC::ClientConnection<ClipboardClientEndpoint, ClipboardServerEndpoint>
|
||||
, public ClipboardServerEndpoint {
|
||||
|
||||
C_OBJECT(ClientConnection);
|
||||
|
||||
public:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
|
||||
virtual ~ClientConnection() override;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
static void for_each_client(Function<void(ClientConnection&)>);
|
||||
|
||||
void notify_about_clipboard_change();
|
||||
|
||||
private:
|
||||
virtual OwnPtr<Messages::ClipboardServer::GreetResponse> handle(const Messages::ClipboardServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::ClipboardServer::GetClipboardDataResponse> handle(const Messages::ClipboardServer::GetClipboardData&) override;
|
||||
virtual OwnPtr<Messages::ClipboardServer::SetClipboardDataResponse> handle(const Messages::ClipboardServer::SetClipboardData&) override;
|
||||
|
||||
RefPtr<SharedBuffer> m_last_sent_buffer;
|
||||
};
|
||||
|
||||
}
|
4
Userland/Services/Clipboard/ClipboardClient.ipc
Normal file
4
Userland/Services/Clipboard/ClipboardClient.ipc
Normal file
|
@ -0,0 +1,4 @@
|
|||
endpoint ClipboardClient = 804
|
||||
{
|
||||
ClipboardDataChanged([UTF8] String mime_type) =|
|
||||
}
|
7
Userland/Services/Clipboard/ClipboardServer.ipc
Normal file
7
Userland/Services/Clipboard/ClipboardServer.ipc
Normal file
|
@ -0,0 +1,7 @@
|
|||
endpoint ClipboardServer = 802
|
||||
{
|
||||
Greet() => (i32 client_id)
|
||||
|
||||
GetClipboardData() => (i32 shbuf_id, i32 data_size, [UTF8] String mime_type, IPC::Dictionary metadata)
|
||||
SetClipboardData(i32 shbuf_id, i32 data_size, [UTF8] String mime_type, IPC::Dictionary metadata) => ()
|
||||
}
|
62
Userland/Services/Clipboard/Storage.cpp
Normal file
62
Userland/Services/Clipboard/Storage.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 <Clipboard/Storage.h>
|
||||
|
||||
namespace Clipboard {
|
||||
|
||||
Storage& Storage::the()
|
||||
{
|
||||
static Storage* s_the;
|
||||
if (!s_the)
|
||||
s_the = new Storage;
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
Storage::Storage()
|
||||
{
|
||||
}
|
||||
|
||||
Storage::~Storage()
|
||||
{
|
||||
}
|
||||
|
||||
void Storage::set_data(NonnullRefPtr<SharedBuffer> data, size_t data_size, const String& mime_type, const HashMap<String, String>& metadata)
|
||||
{
|
||||
dbg() << "Storage::set_data <- [" << mime_type << "] " << data->data<void>() << " (" << data_size << " bytes)";
|
||||
for (auto& it : metadata) {
|
||||
dbg() << " " << it.key << ": " << it.value;
|
||||
}
|
||||
m_shared_buffer = move(data);
|
||||
m_data_size = data_size;
|
||||
m_mime_type = mime_type;
|
||||
m_metadata = metadata;
|
||||
|
||||
if (on_content_change)
|
||||
on_content_change();
|
||||
}
|
||||
|
||||
}
|
73
Userland/Services/Clipboard/Storage.h
Normal file
73
Userland/Services/Clipboard/Storage.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <AK/String.h>
|
||||
|
||||
namespace Clipboard {
|
||||
|
||||
class Storage {
|
||||
public:
|
||||
static Storage& the();
|
||||
~Storage();
|
||||
|
||||
bool has_data() const { return m_shared_buffer; }
|
||||
|
||||
const String& mime_type() const { return m_mime_type; }
|
||||
const HashMap<String, String>& metadata() const { return m_metadata; }
|
||||
|
||||
const u8* data() const
|
||||
{
|
||||
if (!has_data())
|
||||
return nullptr;
|
||||
return m_shared_buffer->data<u8>();
|
||||
}
|
||||
|
||||
size_t data_size() const
|
||||
{
|
||||
if (has_data())
|
||||
return m_data_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_data(NonnullRefPtr<SharedBuffer>, size_t data_size, const String& mime_type, const HashMap<String, String>& metadata);
|
||||
|
||||
Function<void()> on_content_change;
|
||||
|
||||
private:
|
||||
Storage();
|
||||
|
||||
String m_mime_type;
|
||||
RefPtr<SharedBuffer> m_shared_buffer;
|
||||
size_t m_data_size { 0 };
|
||||
HashMap<String, String> m_metadata;
|
||||
};
|
||||
|
||||
}
|
76
Userland/Services/Clipboard/main.cpp
Normal file
76
Userland/Services/Clipboard/main.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* 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 <Clipboard/ClientConnection.h>
|
||||
#include <Clipboard/Storage.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
if (pledge("stdio shared_buffer accept unix rpath cpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
Core::EventLoop event_loop;
|
||||
if (pledge("stdio shared_buffer unix accept", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto server = Core::LocalServer::construct();
|
||||
bool ok = server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
|
||||
if (pledge("stdio shared_buffer accept", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
server->on_ready_to_accept = [&] {
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
dbgln("Clipboard: accept failed.");
|
||||
return;
|
||||
}
|
||||
static int s_next_client_id = 0;
|
||||
int client_id = ++s_next_client_id;
|
||||
IPC::new_client_connection<Clipboard::ClientConnection>(client_socket.release_nonnull(), client_id);
|
||||
};
|
||||
|
||||
Clipboard::Storage::the().on_content_change = [&] {
|
||||
Clipboard::ClientConnection::for_each_client([&](auto& client) {
|
||||
client.notify_about_clipboard_change();
|
||||
});
|
||||
};
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
6
Userland/Services/CrashDaemon/CMakeLists.txt
Normal file
6
Userland/Services/CrashDaemon/CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(CrashDaemon)
|
||||
target_link_libraries(CrashDaemon LibC LibCore LibCoreDump)
|
94
Userland/Services/CrashDaemon/main.cpp
Normal file
94
Userland/Services/CrashDaemon/main.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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/LexicalPath.h>
|
||||
#include <LibCore/DirectoryWatcher.h>
|
||||
#include <LibCoreDump/Backtrace.h>
|
||||
#include <LibCoreDump/Reader.h>
|
||||
#include <serenity.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void wait_until_coredump_is_ready(const String& coredump_path)
|
||||
{
|
||||
while (true) {
|
||||
struct stat statbuf;
|
||||
if (stat(coredump_path.characters(), &statbuf) < 0) {
|
||||
perror("stat");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
if (statbuf.st_mode & 0400) // Check if readable
|
||||
break;
|
||||
|
||||
usleep(10000); // sleep for 10ms
|
||||
}
|
||||
}
|
||||
|
||||
static void print_backtrace(const String& coredump_path)
|
||||
{
|
||||
auto coredump = CoreDump::Reader::create(coredump_path);
|
||||
if (!coredump) {
|
||||
dbgln("Could not open coredump '{}'", coredump_path);
|
||||
return;
|
||||
}
|
||||
for (auto& entry : coredump->backtrace().entries())
|
||||
dbgln("{}", entry.to_string(true));
|
||||
}
|
||||
|
||||
static void launch_crash_reporter(const String& coredump_path)
|
||||
{
|
||||
pid_t child;
|
||||
const char* argv[] = { "CrashReporter", coredump_path.characters(), nullptr, nullptr };
|
||||
if ((errno = posix_spawn(&child, "/bin/CrashReporter", nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
||||
perror("posix_spawn");
|
||||
} else {
|
||||
if (disown(child) < 0)
|
||||
perror("disown");
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
if (pledge("stdio rpath proc exec", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::DirectoryWatcher watcher { "/tmp/coredump" };
|
||||
while (true) {
|
||||
auto event = watcher.wait_for_event();
|
||||
ASSERT(event.has_value());
|
||||
if (event.value().type != Core::DirectoryWatcher::Event::Type::ChildAdded)
|
||||
continue;
|
||||
auto coredump_path = event.value().child_path;
|
||||
dbgln("New coredump file: {}", coredump_path);
|
||||
wait_until_coredump_is_ready(coredump_path);
|
||||
print_backtrace(coredump_path);
|
||||
launch_crash_reporter(coredump_path);
|
||||
}
|
||||
}
|
8
Userland/Services/DHCPClient/CMakeLists.txt
Normal file
8
Userland/Services/DHCPClient/CMakeLists.txt
Normal file
|
@ -0,0 +1,8 @@
|
|||
set(SOURCES
|
||||
DHCPv4Client.cpp
|
||||
DHCPv4.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(DHCPClient)
|
||||
target_link_libraries(DHCPClient LibCore)
|
59
Userland/Services/DHCPClient/DHCPv4.cpp
Normal file
59
Userland/Services/DHCPClient/DHCPv4.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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 "DHCPv4.h"
|
||||
|
||||
//#define DHCPV4_DEBUG
|
||||
|
||||
ParsedDHCPv4Options DHCPv4Packet::parse_options() const
|
||||
{
|
||||
ParsedDHCPv4Options options;
|
||||
for (size_t index = 4; index < DHCPV4_OPTION_FIELD_MAX_LENGTH; ++index) {
|
||||
auto opt_name = *(const DHCPOption*)&m_options[index];
|
||||
switch (opt_name) {
|
||||
case DHCPOption::Pad:
|
||||
continue;
|
||||
case DHCPOption::End:
|
||||
goto escape;
|
||||
default:
|
||||
++index;
|
||||
auto length = m_options[index];
|
||||
if ((size_t)length > DHCPV4_OPTION_FIELD_MAX_LENGTH - index) {
|
||||
dbg() << "Bogus option length " << length << " assuming forgotten END";
|
||||
break;
|
||||
}
|
||||
#ifdef DHCPV4_DEBUG
|
||||
dbg() << "DHCP Option " << (u8)opt_name << " with length " << length;
|
||||
#endif
|
||||
++index;
|
||||
options.options.set(opt_name, { length, &m_options[index] });
|
||||
index += length - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
escape:;
|
||||
return options;
|
||||
}
|
299
Userland/Services/DHCPClient/DHCPv4.h
Normal file
299
Userland/Services/DHCPClient/DHCPv4.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <AK/MACAddress.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Traits.h>
|
||||
#include <AK/Types.h>
|
||||
#include <string.h>
|
||||
|
||||
enum class DHCPv4Flags : u16 {
|
||||
Broadcast = 1,
|
||||
/* everything else is reserved and must be zero */
|
||||
};
|
||||
|
||||
enum class DHCPv4Op : u8 {
|
||||
BootRequest = 1,
|
||||
BootReply = 2
|
||||
};
|
||||
|
||||
enum class DHCPOption : u8 {
|
||||
// BOOTP
|
||||
Pad = 0,
|
||||
SubnetMask,
|
||||
TimeOffset,
|
||||
Router,
|
||||
TimeServer,
|
||||
NameServer,
|
||||
DomainNameServer,
|
||||
LogServer,
|
||||
CookieServer,
|
||||
LPRServer,
|
||||
ImpressServer,
|
||||
ResourceLocationServer,
|
||||
HostName,
|
||||
BootFileSize,
|
||||
MeritDumpFile,
|
||||
DomainName,
|
||||
SwapServer,
|
||||
RootPath,
|
||||
ExtensionsPath,
|
||||
IPForwardingEnableDisable,
|
||||
NonLocalSourceRoutingEnableDisable,
|
||||
PolicyFilter,
|
||||
MaximumDatagramReassemblySize,
|
||||
DefaultIPTTL,
|
||||
PathMTUAgingTimeout,
|
||||
PathMTUPlateauTable,
|
||||
InterfaceMTU,
|
||||
AllSubnetsAreLocal,
|
||||
BroadcastAddress,
|
||||
PerformMaskDiscovery,
|
||||
MaskSupplier,
|
||||
PerformRouterDiscovery,
|
||||
RouterSolicitationAddress,
|
||||
StaticRoute,
|
||||
TrailerEncapsulation,
|
||||
ARPCacheTimeout,
|
||||
EthernetEncapsulation,
|
||||
TCPDefaultTTL,
|
||||
TCPKeepaliveInterval,
|
||||
TCPKeepaliveGarbage,
|
||||
NetworkInformationServiceDomain,
|
||||
NetworkInformationServers,
|
||||
NetworkTimeProtocolServers,
|
||||
VendorSpecificInformation,
|
||||
NetBIOSOverTCPIPNameServer,
|
||||
NetBIOSOverTCPIPDatagramDistributionServer,
|
||||
NetBIOSOverTCPIPNodeType,
|
||||
NetBIOSOverTCPIPScope,
|
||||
XWindowSystemFontServer, // wow
|
||||
XWindowSystemDisplayManager,
|
||||
// DHCP
|
||||
RequestedIPAddress = 50,
|
||||
IPAddressLeaseTime,
|
||||
OptionOverload,
|
||||
DHCPMessageType,
|
||||
ServerIdentifier,
|
||||
ParameterRequestList,
|
||||
Message,
|
||||
MaximumDHCPMessageSize,
|
||||
RenewalT1Time,
|
||||
RenewalT2Time,
|
||||
ClassIdentifier,
|
||||
ClientIdentifier,
|
||||
End = 255
|
||||
};
|
||||
|
||||
enum class DHCPMessageType : u8 {
|
||||
DHCPDiscover = 1,
|
||||
DHCPOffer,
|
||||
DHCPRequest,
|
||||
DHCPDecline,
|
||||
DHCPAck,
|
||||
DHCPNak,
|
||||
DHCPRelease,
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<DHCPOption> : public AK::GenericTraits<DHCPOption> {
|
||||
static constexpr bool is_trivial() { return true; }
|
||||
static unsigned hash(DHCPOption u) { return int_hash((u8)u); }
|
||||
};
|
||||
|
||||
struct ParsedDHCPv4Options {
|
||||
template<typename T>
|
||||
Optional<const T> get(DHCPOption option_name) const
|
||||
{
|
||||
auto option = options.get(option_name);
|
||||
if (!option.has_value()) {
|
||||
return {};
|
||||
}
|
||||
auto& value = option.value();
|
||||
if (value.length != sizeof(T))
|
||||
return {};
|
||||
return *(const T*)value.value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Vector<T> get_many(DHCPOption option_name, size_t max_number) const
|
||||
{
|
||||
Vector<T> values;
|
||||
|
||||
auto option = options.get(option_name);
|
||||
if (!option.has_value()) {
|
||||
return {};
|
||||
}
|
||||
auto& value = option.value();
|
||||
if (value.length < sizeof(T))
|
||||
return {};
|
||||
|
||||
for (size_t i = 0; i < max_number; ++i) {
|
||||
auto offset = i * sizeof(T);
|
||||
if (offset >= value.length)
|
||||
break;
|
||||
values.append(*(T*)((u8*)const_cast<void*>(value.value) + offset));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
String to_string() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("DHCP Options (");
|
||||
builder.appendf("%zu", options.size());
|
||||
builder.append(" entries)\n");
|
||||
for (auto& opt : options) {
|
||||
builder.appendf("\toption %d (%d bytes):", (u8)opt.key, (u8)opt.value.length);
|
||||
for (auto i = 0; i < opt.value.length; ++i)
|
||||
builder.appendf(" %u ", ((const u8*)opt.value.value)[i]);
|
||||
builder.append('\n');
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
struct DHCPOptionValue {
|
||||
u8 length;
|
||||
const void* value;
|
||||
};
|
||||
|
||||
HashMap<DHCPOption, DHCPOptionValue> options;
|
||||
};
|
||||
|
||||
constexpr auto DHCPV4_OPTION_FIELD_MAX_LENGTH = 312;
|
||||
|
||||
class [[gnu::packed]] DHCPv4Packet {
|
||||
public:
|
||||
u8 op() const { return m_op; }
|
||||
void set_op(DHCPv4Op op) { m_op = (u8)op; }
|
||||
|
||||
u8 htype() const { return m_htype; }
|
||||
void set_htype(u8 htype) { m_htype = htype; }
|
||||
|
||||
u8 hlen() const { return m_hlen; }
|
||||
void set_hlen(u8 hlen) { m_hlen = hlen; }
|
||||
|
||||
u8 hops() const { return m_hops; }
|
||||
void set_hops(u8 hops) { m_hops = hops; }
|
||||
|
||||
u32 xid() const { return m_xid; }
|
||||
void set_xid(u32 xid) { m_xid = xid; }
|
||||
|
||||
u16 secs() const { return m_secs; }
|
||||
void set_secs(u16 secs) { m_secs = secs; }
|
||||
|
||||
u16 flags() const { return m_flags; }
|
||||
void set_flags(DHCPv4Flags flags) { m_flags = (u16)flags; }
|
||||
|
||||
const IPv4Address& ciaddr() const { return m_ciaddr; }
|
||||
const IPv4Address& yiaddr() const { return m_yiaddr; }
|
||||
const IPv4Address& siaddr() const { return m_siaddr; }
|
||||
const IPv4Address& giaddr() const { return m_giaddr; }
|
||||
|
||||
IPv4Address& ciaddr() { return m_ciaddr; }
|
||||
IPv4Address& yiaddr() { return m_yiaddr; }
|
||||
IPv4Address& siaddr() { return m_siaddr; }
|
||||
IPv4Address& giaddr() { return m_giaddr; }
|
||||
|
||||
u8* options() { return m_options; }
|
||||
ParsedDHCPv4Options parse_options() const;
|
||||
|
||||
const MACAddress& chaddr() const { return *(const MACAddress*)&m_chaddr[0]; }
|
||||
void set_chaddr(const MACAddress& mac) { *(MACAddress*)&m_chaddr[0] = mac; }
|
||||
|
||||
StringView sname() const { return { (const char*)&m_sname[0] }; }
|
||||
StringView file() const { return { (const char*)&m_file[0] }; }
|
||||
|
||||
private:
|
||||
NetworkOrdered<u8> m_op;
|
||||
NetworkOrdered<u8> m_htype;
|
||||
NetworkOrdered<u8> m_hlen;
|
||||
NetworkOrdered<u8> m_hops;
|
||||
NetworkOrdered<u32> m_xid;
|
||||
NetworkOrdered<u16> m_secs;
|
||||
NetworkOrdered<u16> m_flags;
|
||||
IPv4Address m_ciaddr;
|
||||
IPv4Address m_yiaddr;
|
||||
IPv4Address m_siaddr;
|
||||
IPv4Address m_giaddr;
|
||||
u8 m_chaddr[16]; // 10 bytes of padding at the end
|
||||
u8 m_sname[64] { 0 };
|
||||
u8 m_file[128] { 0 };
|
||||
u8 m_options[DHCPV4_OPTION_FIELD_MAX_LENGTH] { 0 }; // variable, less than 312 bytes
|
||||
};
|
||||
|
||||
class DHCPv4PacketBuilder {
|
||||
public:
|
||||
DHCPv4PacketBuilder()
|
||||
: m_buffer(ByteBuffer::create_zeroed(sizeof(DHCPv4Packet)))
|
||||
{
|
||||
auto* options = peek().options();
|
||||
// set the magic DHCP cookie value
|
||||
options[0] = 99;
|
||||
options[1] = 130;
|
||||
options[2] = 83;
|
||||
options[3] = 99;
|
||||
}
|
||||
|
||||
void add_option(DHCPOption option, u8 length, const void* data)
|
||||
{
|
||||
ASSERT(m_can_add);
|
||||
// we need enough space to fit the option value, its length, and its data
|
||||
ASSERT(next_option_offset + length + 2 < DHCPV4_OPTION_FIELD_MAX_LENGTH);
|
||||
|
||||
auto* options = peek().options();
|
||||
options[next_option_offset++] = (u8)option;
|
||||
memcpy(options + next_option_offset, &length, 1);
|
||||
next_option_offset++;
|
||||
memcpy(options + next_option_offset, data, length);
|
||||
next_option_offset += length;
|
||||
}
|
||||
|
||||
void set_message_type(DHCPMessageType type) { add_option(DHCPOption::DHCPMessageType, 1, &type); }
|
||||
|
||||
DHCPv4Packet& peek() { return *(DHCPv4Packet*)m_buffer.data(); }
|
||||
DHCPv4Packet& build()
|
||||
{
|
||||
add_option(DHCPOption::End, 0, nullptr);
|
||||
m_can_add = false;
|
||||
return *(DHCPv4Packet*)m_buffer.data();
|
||||
}
|
||||
size_t size() const { return m_buffer.size(); }
|
||||
|
||||
private:
|
||||
ByteBuffer m_buffer;
|
||||
size_t next_option_offset { 4 };
|
||||
bool m_can_add { true };
|
||||
};
|
289
Userland/Services/DHCPClient/DHCPv4Client.cpp
Normal file
289
Userland/Services/DHCPClient/DHCPv4Client.cpp
Normal file
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* 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 "DHCPv4Client.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Function.h>
|
||||
#include <LibCore/SocketAddress.h>
|
||||
#include <LibCore/Timer.h>
|
||||
#include <stdio.h>
|
||||
|
||||
//#define DHCPV4CLIENT_DEBUG
|
||||
|
||||
static void send(const InterfaceDescriptor& iface, const DHCPv4Packet& packet, Core::Object*)
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0) {
|
||||
dbg() << "ERROR: socket :: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, iface.m_ifname.characters(), IFNAMSIZ) < 0) {
|
||||
dbg() << "ERROR: setsockopt(SO_BINDTODEVICE) :: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
sockaddr_in dst;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
dst.sin_family = AF_INET;
|
||||
dst.sin_port = htons(67);
|
||||
dst.sin_addr.s_addr = IPv4Address { 255, 255, 255, 255 }.to_u32();
|
||||
memset(&dst.sin_zero, 0, sizeof(dst.sin_zero));
|
||||
|
||||
auto rc = sendto(fd, &packet, sizeof(packet), 0, (sockaddr*)&dst, sizeof(dst));
|
||||
if (rc < 0) {
|
||||
dbg() << "sendto failed with " << strerror(errno);
|
||||
// FIXME: what do we do here?
|
||||
}
|
||||
}
|
||||
|
||||
static void set_params(const InterfaceDescriptor& iface, const IPv4Address& ipv4_addr, const IPv4Address& netmask, const IPv4Address& gateway)
|
||||
{
|
||||
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (fd < 0) {
|
||||
dbg() << "ERROR: socket :: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
|
||||
bool fits = iface.m_ifname.copy_characters_to_buffer(ifr.ifr_name, IFNAMSIZ);
|
||||
if (!fits) {
|
||||
dbgln("Interface name doesn't fit into IFNAMSIZ!");
|
||||
return;
|
||||
}
|
||||
|
||||
// set the IP address
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
((sockaddr_in&)ifr.ifr_addr).sin_addr.s_addr = ipv4_addr.to_in_addr_t();
|
||||
|
||||
if (ioctl(fd, SIOCSIFADDR, &ifr) < 0) {
|
||||
dbg() << "ERROR: ioctl(SIOCSIFADDR) :: " << strerror(errno);
|
||||
}
|
||||
|
||||
// set the network mask
|
||||
((sockaddr_in&)ifr.ifr_netmask).sin_addr.s_addr = netmask.to_in_addr_t();
|
||||
|
||||
if (ioctl(fd, SIOCSIFNETMASK, &ifr) < 0) {
|
||||
dbg() << "ERROR: ioctl(SIOCSIFNETMASK) :: " << strerror(errno);
|
||||
}
|
||||
|
||||
// set the default gateway
|
||||
struct rtentry rt;
|
||||
memset(&rt, 0, sizeof(rt));
|
||||
|
||||
rt.rt_dev = const_cast<char*>(iface.m_ifname.characters());
|
||||
rt.rt_gateway.sa_family = AF_INET;
|
||||
((sockaddr_in&)rt.rt_gateway).sin_addr.s_addr = gateway.to_in_addr_t();
|
||||
rt.rt_flags = RTF_UP | RTF_GATEWAY;
|
||||
|
||||
if (ioctl(fd, SIOCADDRT, &rt) < 0) {
|
||||
dbg() << "Error: ioctl(SIOCADDRT) :: " << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
DHCPv4Client::DHCPv4Client(Vector<InterfaceDescriptor> ifnames)
|
||||
: m_ifnames(ifnames)
|
||||
{
|
||||
m_server = Core::UDPServer::construct(this);
|
||||
m_server->on_ready_to_receive = [this] {
|
||||
auto buffer = m_server->receive(sizeof(DHCPv4Packet));
|
||||
dbg() << "Received " << buffer.size() << " bytes";
|
||||
if (buffer.size() != sizeof(DHCPv4Packet)) {
|
||||
dbg() << "we expected " << sizeof(DHCPv4Packet) << " bytes, this is a bad packet";
|
||||
return;
|
||||
}
|
||||
auto& packet = *(DHCPv4Packet*)buffer.data();
|
||||
process_incoming(packet);
|
||||
};
|
||||
|
||||
if (!m_server->bind({}, 68)) {
|
||||
dbgln("The server we just created somehow came already bound, refusing to continue");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
for (auto& iface : m_ifnames)
|
||||
dhcp_discover(iface);
|
||||
}
|
||||
|
||||
DHCPv4Client::~DHCPv4Client()
|
||||
{
|
||||
}
|
||||
|
||||
void DHCPv4Client::handle_offer(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
|
||||
{
|
||||
dbg() << "We were offered " << packet.yiaddr().to_string() << " for " << options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(0);
|
||||
auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
|
||||
if (!transaction) {
|
||||
dbg() << "we're not looking for " << packet.xid();
|
||||
return;
|
||||
}
|
||||
if (transaction->has_ip)
|
||||
return;
|
||||
if (transaction->accepted_offer) {
|
||||
// we've accepted someone's offer, but they haven't given us an ack
|
||||
// TODO: maybe record this offer?
|
||||
return;
|
||||
}
|
||||
// TAKE IT...
|
||||
transaction->offered_lease_time = options.get<u32>(DHCPOption::IPAddressLeaseTime).value();
|
||||
dhcp_request(*transaction, packet);
|
||||
}
|
||||
|
||||
void DHCPv4Client::handle_ack(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
|
||||
{
|
||||
#ifdef DHCPV4CLIENT_DEBUG
|
||||
dbg() << "The DHCP server handed us " << packet.yiaddr().to_string();
|
||||
dbg() << "Here are the options: " << options.to_string();
|
||||
#endif
|
||||
auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
|
||||
if (!transaction) {
|
||||
dbg() << "we're not looking for " << packet.xid();
|
||||
return;
|
||||
}
|
||||
transaction->has_ip = true;
|
||||
auto& interface = transaction->interface;
|
||||
auto new_ip = packet.yiaddr();
|
||||
auto lease_time = AK::convert_between_host_and_network_endian(options.get<u32>(DHCPOption::IPAddressLeaseTime).value_or(transaction->offered_lease_time));
|
||||
// set a timer for the duration of the lease, we shall renew if needed
|
||||
Core::Timer::create_single_shot(
|
||||
lease_time * 1000,
|
||||
[this, transaction, interface = InterfaceDescriptor { interface }, new_ip] {
|
||||
transaction->accepted_offer = false;
|
||||
transaction->has_ip = false;
|
||||
dhcp_discover(interface, new_ip);
|
||||
},
|
||||
this);
|
||||
set_params(transaction->interface, new_ip, options.get<IPv4Address>(DHCPOption::SubnetMask).value(), options.get_many<IPv4Address>(DHCPOption::Router, 1).first());
|
||||
}
|
||||
|
||||
void DHCPv4Client::handle_nak(const DHCPv4Packet& packet, const ParsedDHCPv4Options& options)
|
||||
{
|
||||
dbg() << "The DHCP server told us to go chase our own tail about " << packet.yiaddr().to_string();
|
||||
dbg() << "Here are the options: " << options.to_string();
|
||||
// make another request a bit later :shrug:
|
||||
auto* transaction = const_cast<DHCPv4Transaction*>(m_ongoing_transactions.get(packet.xid()).value_or(nullptr));
|
||||
if (!transaction) {
|
||||
dbg() << "we're not looking for " << packet.xid();
|
||||
return;
|
||||
}
|
||||
transaction->accepted_offer = false;
|
||||
transaction->has_ip = false;
|
||||
auto& iface = transaction->interface;
|
||||
Core::Timer::create_single_shot(
|
||||
10000,
|
||||
[this, iface = InterfaceDescriptor { iface }] {
|
||||
dhcp_discover(iface);
|
||||
},
|
||||
this);
|
||||
}
|
||||
|
||||
void DHCPv4Client::process_incoming(const DHCPv4Packet& packet)
|
||||
{
|
||||
auto options = packet.parse_options();
|
||||
#ifdef DHCPV4CLIENT_DEBUG
|
||||
dbg() << "Here are the options: " << options.to_string();
|
||||
#endif
|
||||
auto value = options.get<DHCPMessageType>(DHCPOption::DHCPMessageType).value();
|
||||
switch (value) {
|
||||
case DHCPMessageType::DHCPOffer:
|
||||
handle_offer(packet, options);
|
||||
break;
|
||||
case DHCPMessageType::DHCPAck:
|
||||
handle_ack(packet, options);
|
||||
break;
|
||||
case DHCPMessageType::DHCPNak:
|
||||
handle_nak(packet, options);
|
||||
break;
|
||||
case DHCPMessageType::DHCPDiscover:
|
||||
case DHCPMessageType::DHCPRequest:
|
||||
case DHCPMessageType::DHCPRelease:
|
||||
// These are not for us
|
||||
// we're just getting them because there are other people on our subnet
|
||||
// broadcasting stuff
|
||||
break;
|
||||
case DHCPMessageType::DHCPDecline:
|
||||
default:
|
||||
dbg() << "I dunno what to do with this " << (u8)value;
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DHCPv4Client::dhcp_discover(const InterfaceDescriptor& iface, IPv4Address previous)
|
||||
{
|
||||
auto transaction_id = rand();
|
||||
#ifdef DHCPV4CLIENT_DEBUG
|
||||
dbg() << "Trying to lease an IP for " << iface.m_ifname << " with ID " << transaction_id;
|
||||
if (!previous.is_zero())
|
||||
dbg() << "going to request the server to hand us " << previous.to_string();
|
||||
#endif
|
||||
DHCPv4PacketBuilder builder;
|
||||
|
||||
DHCPv4Packet& packet = builder.peek();
|
||||
packet.set_op(DHCPv4Op::BootRequest);
|
||||
packet.set_htype(1); // 10mb ethernet
|
||||
packet.set_hlen(sizeof(MACAddress));
|
||||
packet.set_xid(transaction_id);
|
||||
packet.set_flags(DHCPv4Flags::Broadcast);
|
||||
packet.ciaddr() = previous;
|
||||
packet.set_chaddr(iface.m_mac_address);
|
||||
packet.set_secs(65535); // we lie
|
||||
|
||||
// set packet options
|
||||
builder.set_message_type(DHCPMessageType::DHCPDiscover);
|
||||
auto& dhcp_packet = builder.build();
|
||||
|
||||
// broadcast the discover request
|
||||
send(iface, dhcp_packet, this);
|
||||
m_ongoing_transactions.set(transaction_id, make<DHCPv4Transaction>(iface));
|
||||
}
|
||||
|
||||
void DHCPv4Client::dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& offer)
|
||||
{
|
||||
auto& iface = transaction.interface;
|
||||
dbg() << "Leasing the IP " << offer.yiaddr().to_string() << " for adapter " << iface.m_ifname;
|
||||
DHCPv4PacketBuilder builder;
|
||||
|
||||
DHCPv4Packet& packet = builder.peek();
|
||||
packet.set_op(DHCPv4Op::BootRequest);
|
||||
packet.set_htype(1); // 10mb ethernet
|
||||
packet.set_hlen(sizeof(MACAddress));
|
||||
packet.set_xid(offer.xid());
|
||||
packet.set_flags(DHCPv4Flags::Broadcast);
|
||||
packet.set_chaddr(iface.m_mac_address);
|
||||
packet.set_secs(65535); // we lie
|
||||
|
||||
// set packet options
|
||||
builder.set_message_type(DHCPMessageType::DHCPRequest);
|
||||
auto& dhcp_packet = builder.build();
|
||||
|
||||
// broadcast the "request" request
|
||||
send(iface, dhcp_packet, this);
|
||||
transaction.accepted_offer = true;
|
||||
}
|
79
Userland/Services/DHCPClient/DHCPv4Client.h
Normal file
79
Userland/Services/DHCPClient/DHCPv4Client.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DHCPv4.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/UDPServer.h>
|
||||
#include <net/if.h>
|
||||
#include <net/route.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
struct InterfaceDescriptor {
|
||||
String m_ifname;
|
||||
MACAddress m_mac_address;
|
||||
};
|
||||
|
||||
struct DHCPv4Transaction {
|
||||
DHCPv4Transaction(InterfaceDescriptor ifname)
|
||||
: interface(ifname)
|
||||
{
|
||||
}
|
||||
|
||||
InterfaceDescriptor interface;
|
||||
bool accepted_offer { false };
|
||||
bool has_ip { false };
|
||||
u32 offered_lease_time { 0 };
|
||||
};
|
||||
|
||||
class DHCPv4Client final : public Core::Object {
|
||||
C_OBJECT(DHCPv4Client)
|
||||
|
||||
public:
|
||||
explicit DHCPv4Client(Vector<InterfaceDescriptor> ifnames);
|
||||
virtual ~DHCPv4Client() override;
|
||||
|
||||
void dhcp_discover(const InterfaceDescriptor& ifname, IPv4Address previous = IPv4Address { 0, 0, 0, 0 });
|
||||
void dhcp_request(DHCPv4Transaction& transaction, const DHCPv4Packet& packet);
|
||||
|
||||
void process_incoming(const DHCPv4Packet& packet);
|
||||
|
||||
bool id_is_registered(u32 id) { return m_ongoing_transactions.contains(id); }
|
||||
|
||||
private:
|
||||
HashMap<u32, OwnPtr<DHCPv4Transaction>> m_ongoing_transactions;
|
||||
Vector<InterfaceDescriptor> m_ifnames;
|
||||
RefPtr<Core::UDPServer> m_server;
|
||||
|
||||
void handle_offer(const DHCPv4Packet&, const ParsedDHCPv4Options&);
|
||||
void handle_ack(const DHCPv4Packet&, const ParsedDHCPv4Options&);
|
||||
void handle_nak(const DHCPv4Packet&, const ParsedDHCPv4Options&);
|
||||
};
|
99
Userland/Services/DHCPClient/main.cpp
Normal file
99
Userland/Services/DHCPClient/main.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 "DHCPv4Client.h"
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static u8 mac_part(const Vector<String>& parts, size_t index)
|
||||
{
|
||||
auto chars = parts.at(index).characters();
|
||||
return (chars[0] - '0') * 16 + (chars[1] - '0');
|
||||
}
|
||||
|
||||
static MACAddress mac_from_string(const String& str)
|
||||
{
|
||||
auto chunks = str.split(':');
|
||||
ASSERT(chunks.size() == 6); // should we...worry about this?
|
||||
return {
|
||||
mac_part(chunks, 0), mac_part(chunks, 1), mac_part(chunks, 2),
|
||||
mac_part(chunks, 3), mac_part(chunks, 4), mac_part(chunks, 5)
|
||||
};
|
||||
}
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
|
||||
{
|
||||
if (pledge("stdio unix inet cpath rpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
|
||||
if (unveil("/proc/net/", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
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());
|
||||
Vector<InterfaceDescriptor> ifnames;
|
||||
json.value().as_array().for_each([&ifnames](auto& value) {
|
||||
auto if_object = value.as_object();
|
||||
|
||||
if (if_object.get("class_name").to_string() == "LoopbackAdapter")
|
||||
return;
|
||||
|
||||
auto name = if_object.get("name").to_string();
|
||||
auto mac = if_object.get("mac_address").to_string();
|
||||
ifnames.append({ name, mac_from_string(mac) });
|
||||
});
|
||||
|
||||
auto client = DHCPv4Client::construct(move(ifnames));
|
||||
|
||||
if (pledge("stdio inet", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
7
Userland/Services/EchoServer/CMakeLists.txt
Normal file
7
Userland/Services/EchoServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(SOURCES
|
||||
Client.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(EchoServer)
|
||||
target_link_libraries(EchoServer LibCore)
|
58
Userland/Services/EchoServer/Client.cpp
Normal file
58
Userland/Services/EchoServer/Client.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 "Client.h"
|
||||
|
||||
Client::Client(int id, RefPtr<Core::TCPSocket> socket)
|
||||
: m_id(id)
|
||||
, m_socket(move(socket))
|
||||
{
|
||||
m_socket->on_ready_to_read = [this] { drain_socket(); };
|
||||
}
|
||||
|
||||
void Client::drain_socket()
|
||||
{
|
||||
NonnullRefPtr<Client> protect(*this);
|
||||
while (m_socket->can_read()) {
|
||||
auto buf = m_socket->read(1024);
|
||||
|
||||
dbg() << "Read " << buf.size() << " bytes: " << buf;
|
||||
|
||||
if (m_socket->eof()) {
|
||||
quit();
|
||||
break;
|
||||
}
|
||||
|
||||
m_socket->write(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::quit()
|
||||
{
|
||||
m_socket->close();
|
||||
if (on_exit)
|
||||
on_exit();
|
||||
}
|
49
Userland/Services/EchoServer/Client.h
Normal file
49
Userland/Services/EchoServer/Client.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/TCPSocket.h>
|
||||
|
||||
class Client : public RefCounted<Client> {
|
||||
public:
|
||||
static NonnullRefPtr<Client> create(int id, RefPtr<Core::TCPSocket> socket)
|
||||
{
|
||||
return adopt(*new Client(id, move(socket)));
|
||||
}
|
||||
|
||||
Function<void()> on_exit;
|
||||
|
||||
protected:
|
||||
Client(int id, RefPtr<Core::TCPSocket> socket);
|
||||
|
||||
void drain_socket();
|
||||
void quit();
|
||||
|
||||
private:
|
||||
int m_id { 0 };
|
||||
RefPtr<Core::TCPSocket> m_socket;
|
||||
};
|
99
Userland/Services/EchoServer/main.cpp
Normal file
99
Userland/Services/EchoServer/main.cpp
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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 "Client.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/TCPServer.h>
|
||||
#include <LibCore/TCPSocket.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (pledge("stdio cpath unix fattr inet id accept", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/tmp/rpc", "rwc") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int port = 7;
|
||||
|
||||
Core::ArgsParser args_parser;
|
||||
args_parser.add_option(port, "Port to listen on", "port", 'p', "port");
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
if ((u16)port != port) {
|
||||
warnln("Invalid port number: {}", port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
|
||||
auto server = Core::TCPServer::construct();
|
||||
|
||||
if (!server->listen({}, port)) {
|
||||
warnln("Listening on 0.0.0.0:{} failed", port);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
HashMap<int, NonnullRefPtr<Client>> clients;
|
||||
int next_id = 0;
|
||||
|
||||
server->on_ready_to_accept = [&next_id, &clients, &server] {
|
||||
int id = next_id++;
|
||||
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
perror("accept");
|
||||
return;
|
||||
}
|
||||
|
||||
outln("Client {} connected", id);
|
||||
|
||||
auto client = Client::create(id, move(client_socket));
|
||||
client->on_exit = [&clients, id] {
|
||||
clients.remove(id);
|
||||
outln("Client {} disconnected", id);
|
||||
};
|
||||
clients.set(id, client);
|
||||
};
|
||||
|
||||
outln("Listening on 0.0.0.0:{}", port);
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
12
Userland/Services/ImageDecoder/CMakeLists.txt
Normal file
12
Userland/Services/ImageDecoder/CMakeLists.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
compile_ipc(ImageDecoderServer.ipc ImageDecoderServerEndpoint.h)
|
||||
compile_ipc(ImageDecoderClient.ipc ImageDecoderClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
main.cpp
|
||||
ImageDecoderServerEndpoint.h
|
||||
ImageDecoderClientEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(ImageDecoder)
|
||||
target_link_libraries(ImageDecoder LibGfx LibIPC)
|
102
Userland/Services/ImageDecoder/ClientConnection.cpp
Normal file
102
Userland/Services/ImageDecoder/ClientConnection.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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/Badge.h>
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <ImageDecoder/ClientConnection.h>
|
||||
#include <ImageDecoder/ImageDecoderClientEndpoint.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/ImageDecoder.h>
|
||||
#include <LibGfx/SystemTheme.h>
|
||||
|
||||
namespace ImageDecoder {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id)
|
||||
: IPC::ClientConnection<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>(*this, move(socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
exit(0);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ImageDecoderServer::GreetResponse> ClientConnection::handle(const Messages::ImageDecoderServer::Greet& message)
|
||||
{
|
||||
set_client_pid(message.client_pid());
|
||||
return make<Messages::ImageDecoderServer::GreetResponse>(client_id(), getpid());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ImageDecoderServer::DecodeImageResponse> ClientConnection::handle(const Messages::ImageDecoderServer::DecodeImage& message)
|
||||
{
|
||||
auto encoded_buffer = SharedBuffer::create_from_shbuf_id(message.encoded_shbuf_id());
|
||||
if (!encoded_buffer) {
|
||||
#ifdef IMAGE_DECODER_DEBUG
|
||||
dbgln("Could not map encoded data buffer");
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
if (message.encoded_size() > (size_t)encoded_buffer->size()) {
|
||||
#ifdef IMAGE_DECODER_DEBUG
|
||||
dbgln("Encoded buffer is smaller than encoded size");
|
||||
#endif
|
||||
return {};
|
||||
}
|
||||
|
||||
#ifdef IMAGE_DECODER_DEBUG
|
||||
dbg() << "Trying to decode " << message.encoded_size() << " bytes of image(?) data in shbuf_id=" << message.encoded_shbuf_id() << " (shbuf size: " << encoded_buffer->size() << ")";
|
||||
#endif
|
||||
|
||||
auto decoder = Gfx::ImageDecoder::create(encoded_buffer->data<u8>(), message.encoded_size());
|
||||
auto bitmap = decoder->bitmap();
|
||||
|
||||
if (!bitmap) {
|
||||
#ifdef IMAGE_DECODER_DEBUG
|
||||
dbgln("Could not decode image from encoded data");
|
||||
#endif
|
||||
return make<Messages::ImageDecoderServer::DecodeImageResponse>(-1, Gfx::IntSize(), (i32)Gfx::BitmapFormat::Invalid, Vector<u32>());
|
||||
}
|
||||
|
||||
// FIXME: We should fix ShareableBitmap so you can send it in responses as well as requests..
|
||||
m_shareable_bitmap = bitmap->to_bitmap_backed_by_shared_buffer();
|
||||
m_shareable_bitmap->shared_buffer()->share_with(client_pid());
|
||||
Vector<u32> palette;
|
||||
if (m_shareable_bitmap->is_indexed()) {
|
||||
palette = m_shareable_bitmap->palette_to_vector();
|
||||
}
|
||||
return make<Messages::ImageDecoderServer::DecodeImageResponse>(m_shareable_bitmap->shbuf_id(), m_shareable_bitmap->size(), (i32)m_shareable_bitmap->format(), palette);
|
||||
}
|
||||
|
||||
}
|
56
Userland/Services/ImageDecoder/ClientConnection.h
Normal file
56
Userland/Services/ImageDecoder/ClientConnection.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <ImageDecoder/Forward.h>
|
||||
#include <ImageDecoder/ImageDecoderClientEndpoint.h>
|
||||
#include <ImageDecoder/ImageDecoderServerEndpoint.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
|
||||
namespace ImageDecoder {
|
||||
|
||||
class ClientConnection final
|
||||
: public IPC::ClientConnection<ImageDecoderClientEndpoint, ImageDecoderServerEndpoint>
|
||||
, public ImageDecoderServerEndpoint {
|
||||
C_OBJECT(ClientConnection);
|
||||
|
||||
public:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
|
||||
~ClientConnection() override;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
private:
|
||||
virtual OwnPtr<Messages::ImageDecoderServer::GreetResponse> handle(const Messages::ImageDecoderServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::ImageDecoderServer::DecodeImageResponse> handle(const Messages::ImageDecoderServer::DecodeImage&) override;
|
||||
|
||||
RefPtr<Gfx::Bitmap> m_shareable_bitmap;
|
||||
};
|
||||
|
||||
}
|
34
Userland/Services/ImageDecoder/Forward.h
Normal file
34
Userland/Services/ImageDecoder/Forward.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace WebContent {
|
||||
|
||||
class ClientConnection;
|
||||
class PageHost;
|
||||
|
||||
}
|
4
Userland/Services/ImageDecoder/ImageDecoderClient.ipc
Normal file
4
Userland/Services/ImageDecoder/ImageDecoderClient.ipc
Normal file
|
@ -0,0 +1,4 @@
|
|||
endpoint ImageDecoderClient = 7002
|
||||
{
|
||||
Dummy() =|
|
||||
}
|
7
Userland/Services/ImageDecoder/ImageDecoderServer.ipc
Normal file
7
Userland/Services/ImageDecoder/ImageDecoderServer.ipc
Normal file
|
@ -0,0 +1,7 @@
|
|||
endpoint ImageDecoderServer = 7001
|
||||
{
|
||||
Greet(i32 client_pid) => (i32 client_id, i32 server_pid)
|
||||
|
||||
DecodeImage(i32 encoded_shbuf_id, u32 encoded_size) => (i32 decoded_shbuf_id, Gfx::IntSize size, i32 bitmap_format, Vector<u32> palette)
|
||||
|
||||
}
|
51
Userland/Services/ImageDecoder/main.cpp
Normal file
51
Userland/Services/ImageDecoder/main.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 <ImageDecoder/ClientConnection.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
Core::EventLoop event_loop;
|
||||
if (pledge("stdio shared_buffer unix", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
||||
IPC::new_client_connection<ImageDecoder::ClientConnection>(socket.release_nonnull(), 1);
|
||||
if (pledge("stdio shared_buffer", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
return event_loop.exec();
|
||||
}
|
13
Userland/Services/LaunchServer/CMakeLists.txt
Normal file
13
Userland/Services/LaunchServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
compile_ipc(LaunchServer.ipc LaunchServerEndpoint.h)
|
||||
compile_ipc(LaunchClient.ipc LaunchClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
Launcher.cpp
|
||||
main.cpp
|
||||
LaunchClientEndpoint.h
|
||||
LaunchServerEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(LaunchServer)
|
||||
target_link_libraries(LaunchServer LibCore LibIPC LibDesktop)
|
159
Userland/Services/LaunchServer/ClientConnection.cpp
Normal file
159
Userland/Services/LaunchServer/ClientConnection.cpp
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>
|
||||
* 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 "ClientConnection.h"
|
||||
#include "Launcher.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/URL.h>
|
||||
#include <LaunchServer/LaunchClientEndpoint.h>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id)
|
||||
: IPC::ClientConnection<LaunchClientEndpoint, LaunchServerEndpoint>(*this, move(client_socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::GreetResponse> ClientConnection::handle(const Messages::LaunchServer::Greet&)
|
||||
{
|
||||
return make<Messages::LaunchServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::OpenURLResponse> ClientConnection::handle(const Messages::LaunchServer::OpenURL& request)
|
||||
{
|
||||
if (!m_allowlist.is_empty()) {
|
||||
bool allowed = false;
|
||||
for (auto& allowed_handler : m_allowlist) {
|
||||
if (allowed_handler.handler_name == request.handler_name()
|
||||
&& (allowed_handler.any_url || allowed_handler.urls.contains_slow(request.url()))) {
|
||||
allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed) {
|
||||
// You are not on the list, go home!
|
||||
did_misbehave(String::formatted("Client requested a combination of handler/URL that was not on the list: '{}' with '{}'", request.handler_name(), request.url()).characters());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
URL url(request.url());
|
||||
auto result = Launcher::the().open_url(url, request.handler_name());
|
||||
return make<Messages::LaunchServer::OpenURLResponse>(result);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> ClientConnection::handle(const Messages::LaunchServer::GetHandlersForURL& request)
|
||||
{
|
||||
URL url(request.url());
|
||||
auto result = Launcher::the().handlers_for_url(url);
|
||||
return make<Messages::LaunchServer::GetHandlersForURLResponse>(result);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> ClientConnection::handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL& request)
|
||||
{
|
||||
URL url(request.url());
|
||||
auto result = Launcher::the().handlers_with_details_for_url(url);
|
||||
return make<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse>(result);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::AddAllowedURLResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedURL& request)
|
||||
{
|
||||
if (m_allowlist_is_sealed) {
|
||||
did_misbehave("Got request to add more allowed handlers after list was sealed");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!request.url().is_valid()) {
|
||||
did_misbehave("Got request to allow invalid URL");
|
||||
return {};
|
||||
}
|
||||
|
||||
m_allowlist.empend(String(), false, Vector<URL> { request.url() });
|
||||
|
||||
return make<Messages::LaunchServer::AddAllowedURLResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL& request)
|
||||
{
|
||||
if (m_allowlist_is_sealed) {
|
||||
did_misbehave("Got request to add more allowed handlers after list was sealed");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (request.handler_name().is_empty()) {
|
||||
did_misbehave("Got request to allow empty handler name");
|
||||
return {};
|
||||
}
|
||||
|
||||
m_allowlist.empend(request.handler_name(), true, Vector<URL>());
|
||||
|
||||
return make<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse> ClientConnection::handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs& request)
|
||||
{
|
||||
if (m_allowlist_is_sealed) {
|
||||
did_misbehave("Got request to add more allowed handlers after list was sealed");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (request.handler_name().is_empty()) {
|
||||
did_misbehave("Got request to allow empty handler name");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (request.urls().is_empty()) {
|
||||
did_misbehave("Got request to allow empty URL list");
|
||||
return {};
|
||||
}
|
||||
|
||||
m_allowlist.empend(request.handler_name(), false, request.urls());
|
||||
|
||||
return make<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse>();
|
||||
}
|
||||
|
||||
OwnPtr<Messages::LaunchServer::SealAllowlistResponse> ClientConnection::handle(const Messages::LaunchServer::SealAllowlist&)
|
||||
{
|
||||
if (m_allowlist_is_sealed) {
|
||||
did_misbehave("Got more than one request to seal the allowed handlers list");
|
||||
return {};
|
||||
}
|
||||
|
||||
return make<Messages::LaunchServer::SealAllowlistResponse>();
|
||||
}
|
||||
|
||||
}
|
64
Userland/Services/LaunchServer/ClientConnection.h
Normal file
64
Userland/Services/LaunchServer/ClientConnection.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LaunchServer/LaunchClientEndpoint.h>
|
||||
#include <LaunchServer/LaunchServerEndpoint.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
class ClientConnection final : public IPC::ClientConnection<LaunchClientEndpoint, LaunchServerEndpoint>
|
||||
, public LaunchServerEndpoint {
|
||||
C_OBJECT(ClientConnection)
|
||||
public:
|
||||
~ClientConnection() override;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
private:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
|
||||
|
||||
virtual OwnPtr<Messages::LaunchServer::GreetResponse> handle(const Messages::LaunchServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::OpenURLResponse> handle(const Messages::LaunchServer::OpenURL&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::GetHandlersForURLResponse> handle(const Messages::LaunchServer::GetHandlersForURL&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::GetHandlersWithDetailsForURLResponse> handle(const Messages::LaunchServer::GetHandlersWithDetailsForURL&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::AddAllowedURLResponse> handle(const Messages::LaunchServer::AddAllowedURL&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithAnyURLResponse> handle(const Messages::LaunchServer::AddAllowedHandlerWithAnyURL&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLsResponse> handle(const Messages::LaunchServer::AddAllowedHandlerWithOnlySpecificURLs&) override;
|
||||
virtual OwnPtr<Messages::LaunchServer::SealAllowlistResponse> handle(const Messages::LaunchServer::SealAllowlist&) override;
|
||||
|
||||
struct AllowlistEntry {
|
||||
String handler_name;
|
||||
bool any_url { false };
|
||||
Vector<URL> urls;
|
||||
};
|
||||
|
||||
Vector<AllowlistEntry> m_allowlist;
|
||||
bool m_allowlist_is_sealed { false };
|
||||
};
|
||||
}
|
4
Userland/Services/LaunchServer/LaunchClient.ipc
Normal file
4
Userland/Services/LaunchServer/LaunchClient.ipc
Normal file
|
@ -0,0 +1,4 @@
|
|||
endpoint LaunchClient = 102
|
||||
{
|
||||
Dummy() =|
|
||||
}
|
12
Userland/Services/LaunchServer/LaunchServer.ipc
Normal file
12
Userland/Services/LaunchServer/LaunchServer.ipc
Normal file
|
@ -0,0 +1,12 @@
|
|||
endpoint LaunchServer = 101
|
||||
{
|
||||
Greet() => (i32 client_id)
|
||||
OpenURL(URL url, String handler_name) => (bool response)
|
||||
GetHandlersForURL(URL url) => (Vector<String> handlers)
|
||||
GetHandlersWithDetailsForURL(URL url) => (Vector<String> handlers_details)
|
||||
|
||||
AddAllowedURL(URL url) => ()
|
||||
AddAllowedHandlerWithAnyURL(String handler_name) => ()
|
||||
AddAllowedHandlerWithOnlySpecificURLs(String handler_name, Vector<URL> urls) => ()
|
||||
SealAllowlist() => ()
|
||||
}
|
309
Userland/Services/LaunchServer/Launcher.cpp
Normal file
309
Userland/Services/LaunchServer/Launcher.cpp
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>, 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 "Launcher.h"
|
||||
#include <AK/Function.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonObjectSerializer.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibDesktop/AppFile.h>
|
||||
#include <serenity.h>
|
||||
#include <spawn.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
static Launcher* s_the;
|
||||
static bool spawn(String executable, String argument);
|
||||
|
||||
String Handler::name_from_executable(const StringView& executable)
|
||||
{
|
||||
auto separator = executable.find_last_of('/');
|
||||
if (separator.has_value()) {
|
||||
auto start = separator.value() + 1;
|
||||
return executable.substring_view(start, executable.length() - start);
|
||||
}
|
||||
return executable;
|
||||
}
|
||||
|
||||
void Handler::from_executable(Type handler_type, const String& executable)
|
||||
{
|
||||
this->handler_type = handler_type;
|
||||
this->name = name_from_executable(executable);
|
||||
this->executable = executable;
|
||||
}
|
||||
|
||||
String Handler::to_details_str() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
JsonObjectSerializer obj { builder };
|
||||
obj.add("executable", executable);
|
||||
obj.add("name", name);
|
||||
switch (handler_type) {
|
||||
case Type::Application:
|
||||
obj.add("type", "app");
|
||||
break;
|
||||
case Type::UserDefault:
|
||||
obj.add("type", "userdefault");
|
||||
break;
|
||||
case Type::UserPreferred:
|
||||
obj.add("type", "userpreferred");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
obj.finish();
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
Launcher::Launcher()
|
||||
{
|
||||
ASSERT(s_the == nullptr);
|
||||
s_the = this;
|
||||
}
|
||||
|
||||
Launcher& Launcher::the()
|
||||
{
|
||||
ASSERT(s_the);
|
||||
return *s_the;
|
||||
}
|
||||
|
||||
void Launcher::load_handlers(const String& af_dir)
|
||||
{
|
||||
Desktop::AppFile::for_each([&](auto af) {
|
||||
auto app_name = af->name();
|
||||
auto app_executable = af->executable();
|
||||
HashTable<String> file_types;
|
||||
for (auto& file_type : af->launcher_file_types())
|
||||
file_types.set(file_type);
|
||||
HashTable<String> protocols;
|
||||
for (auto& protocol : af->launcher_protocols())
|
||||
protocols.set(protocol);
|
||||
m_handlers.set(app_executable, { Handler::Type::Default, app_name, app_executable, file_types, protocols });
|
||||
},
|
||||
af_dir);
|
||||
}
|
||||
|
||||
void Launcher::load_config(const Core::ConfigFile& cfg)
|
||||
{
|
||||
for (auto key : cfg.keys("FileType")) {
|
||||
auto handler = cfg.read_entry("FileType", key).trim_whitespace();
|
||||
if (handler.is_empty())
|
||||
continue;
|
||||
m_file_handlers.set(key.to_lowercase(), handler);
|
||||
}
|
||||
|
||||
for (auto key : cfg.keys("Protocol")) {
|
||||
auto handler = cfg.read_entry("Protocol", key).trim_whitespace();
|
||||
if (handler.is_empty())
|
||||
continue;
|
||||
m_protocol_handlers.set(key.to_lowercase(), handler);
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> Launcher::handlers_for_url(const URL& url)
|
||||
{
|
||||
Vector<String> handlers;
|
||||
if (url.protocol() == "file") {
|
||||
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool {
|
||||
handlers.append(handler.executable);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool {
|
||||
if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) {
|
||||
handlers.append(handler.executable);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
|
||||
Vector<String> Launcher::handlers_with_details_for_url(const URL& url)
|
||||
{
|
||||
Vector<String> handlers;
|
||||
if (url.protocol() == "file") {
|
||||
for_each_handler_for_path(url.path(), [&](auto& handler) -> bool {
|
||||
handlers.append(handler.to_details_str());
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
for_each_handler(url.protocol(), m_protocol_handlers, [&](const auto& handler) -> bool {
|
||||
if (handler.handler_type != Handler::Type::Default || handler.protocols.contains(url.protocol())) {
|
||||
handlers.append(handler.to_details_str());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
|
||||
bool Launcher::open_url(const URL& url, const String& handler_name)
|
||||
{
|
||||
if (!handler_name.is_null())
|
||||
return open_with_handler_name(url, handler_name);
|
||||
|
||||
if (url.protocol() == "file")
|
||||
return open_file_url(url);
|
||||
|
||||
return open_with_user_preferences(m_protocol_handlers, url.protocol(), url.to_string());
|
||||
}
|
||||
|
||||
bool Launcher::open_with_handler_name(const URL& url, const String& handler_name)
|
||||
{
|
||||
auto handler_optional = m_handlers.get(handler_name);
|
||||
if (!handler_optional.has_value())
|
||||
return false;
|
||||
|
||||
auto& handler = handler_optional.value();
|
||||
String argument;
|
||||
if (url.protocol() == "file")
|
||||
argument = url.path();
|
||||
else
|
||||
argument = url.to_string();
|
||||
return spawn(handler.executable, argument);
|
||||
}
|
||||
|
||||
bool spawn(String executable, String argument)
|
||||
{
|
||||
pid_t child_pid;
|
||||
const char* argv[] = { executable.characters(), argument.characters(), nullptr };
|
||||
if ((errno = posix_spawn(&child_pid, executable.characters(), nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
||||
perror("posix_spawn");
|
||||
return false;
|
||||
} else {
|
||||
if (disown(child_pid) < 0)
|
||||
perror("disown");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Handler Launcher::get_handler_for_executable(Handler::Type handler_type, const String& executable) const
|
||||
{
|
||||
Handler handler;
|
||||
auto existing_handler = m_handlers.get(executable);
|
||||
if (existing_handler.has_value()) {
|
||||
handler = existing_handler.value();
|
||||
handler.handler_type = handler_type;
|
||||
} else {
|
||||
handler.from_executable(handler_type, executable);
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
bool Launcher::open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program)
|
||||
{
|
||||
auto program_path = user_preferences.get(key);
|
||||
if (program_path.has_value())
|
||||
return spawn(program_path.value(), argument);
|
||||
|
||||
// There wasn't a handler for this, so try the fallback instead
|
||||
program_path = user_preferences.get("*");
|
||||
if (program_path.has_value())
|
||||
return spawn(program_path.value(), argument);
|
||||
|
||||
// Absolute worst case, try the provided default program, if any
|
||||
if (!default_program.is_empty())
|
||||
return spawn(default_program, argument);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Launcher::for_each_handler(const String& key, HashMap<String, String>& user_preference, Function<bool(const Handler&)> f)
|
||||
{
|
||||
auto user_preferred = user_preference.get(key);
|
||||
if (user_preferred.has_value())
|
||||
f(get_handler_for_executable(Handler::Type::UserPreferred, user_preferred.value()));
|
||||
|
||||
size_t counted = 0;
|
||||
for (auto& handler : m_handlers) {
|
||||
// Skip over the existing item in the list
|
||||
if (user_preferred.has_value() && user_preferred.value() == handler.value.executable)
|
||||
continue;
|
||||
if (f(handler.value))
|
||||
counted++;
|
||||
}
|
||||
|
||||
auto user_default = user_preference.get("*");
|
||||
if (counted == 0 && user_default.has_value())
|
||||
f(get_handler_for_executable(Handler::Type::UserDefault, user_default.value()));
|
||||
}
|
||||
|
||||
void Launcher::for_each_handler_for_path(const String& path, Function<bool(const Handler&)> f)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path.characters(), &st) < 0) {
|
||||
perror("stat");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Make directory opening configurable
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
f(get_handler_for_executable(Handler::Type::Default, "/bin/FileManager"));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((st.st_mode & S_IFMT) == S_IFREG && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))
|
||||
f(get_handler_for_executable(Handler::Type::Application, path));
|
||||
|
||||
auto extension = LexicalPath(path).extension().to_lowercase();
|
||||
|
||||
for_each_handler(extension, m_file_handlers, [&](const auto& handler) -> bool {
|
||||
if (handler.handler_type != Handler::Type::Default || handler.file_types.contains(extension))
|
||||
return f(handler);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool Launcher::open_file_url(const URL& url)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(url.path().characters(), &st) < 0) {
|
||||
perror("stat");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Make directory opening configurable
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return spawn("/bin/FileManager", url.path());
|
||||
|
||||
if ((st.st_mode & S_IFMT) == S_IFREG && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
|
||||
return spawn(url.path(), {});
|
||||
|
||||
auto extension_parts = url.path().to_lowercase().split('.');
|
||||
String extension = {};
|
||||
if (extension_parts.size() > 1)
|
||||
extension = extension_parts.last();
|
||||
return open_with_user_preferences(m_file_handlers, extension, url.path(), "/bin/TextEditor");
|
||||
}
|
||||
}
|
78
Userland/Services/LaunchServer/Launcher.h
Normal file
78
Userland/Services/LaunchServer/Launcher.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/URL.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibDesktop/AppFile.h>
|
||||
|
||||
namespace LaunchServer {
|
||||
|
||||
struct Handler {
|
||||
enum class Type {
|
||||
Default = 0,
|
||||
Application,
|
||||
UserPreferred,
|
||||
UserDefault
|
||||
};
|
||||
Type handler_type;
|
||||
String name;
|
||||
String executable;
|
||||
HashTable<String> file_types {};
|
||||
HashTable<String> protocols {};
|
||||
|
||||
static String name_from_executable(const StringView&);
|
||||
void from_executable(Type, const String&);
|
||||
String to_details_str() const;
|
||||
};
|
||||
|
||||
class Launcher {
|
||||
public:
|
||||
Launcher();
|
||||
static Launcher& the();
|
||||
|
||||
void load_handlers(const String& af_dir = Desktop::AppFile::APP_FILES_DIRECTORY);
|
||||
void load_config(const Core::ConfigFile&);
|
||||
bool open_url(const URL&, const String& handler_name);
|
||||
Vector<String> handlers_for_url(const URL&);
|
||||
Vector<String> handlers_with_details_for_url(const URL&);
|
||||
|
||||
private:
|
||||
HashMap<String, Handler> m_handlers;
|
||||
HashMap<String, String> m_protocol_handlers;
|
||||
HashMap<String, String> m_file_handlers;
|
||||
|
||||
Handler get_handler_for_executable(Handler::Type, const String&) const;
|
||||
void for_each_handler(const String& key, HashMap<String, String>& user_preferences, Function<bool(const Handler&)> f);
|
||||
void for_each_handler_for_path(const String&, Function<bool(const Handler&)> f);
|
||||
bool open_file_url(const URL&);
|
||||
bool open_with_user_preferences(const HashMap<String, String>& user_preferences, const String key, const String argument, const String default_program = {});
|
||||
bool open_with_handler_name(const URL&, const String& handler_name);
|
||||
};
|
||||
}
|
65
Userland/Services/LaunchServer/main.cpp
Normal file
65
Userland/Services/LaunchServer/main.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Nicholas Hollett <niax@niax.co.uk>
|
||||
* 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 "ClientConnection.h"
|
||||
#include "Launcher.h"
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
|
||||
{
|
||||
Core::EventLoop event_loop;
|
||||
auto server = Core::LocalServer::construct();
|
||||
|
||||
auto launcher = LaunchServer::Launcher();
|
||||
|
||||
launcher.load_handlers();
|
||||
launcher.load_config(Core::ConfigFile::get_for_app("LaunchServer"));
|
||||
|
||||
if (pledge("stdio accept rpath proc exec", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ok = server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
server->on_ready_to_accept = [&] {
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
dbgln("LaunchServer: accept failed.");
|
||||
return;
|
||||
}
|
||||
static int s_next_client_id = 0;
|
||||
int client_id = ++s_next_client_id;
|
||||
dbgln("Received connection");
|
||||
IPC::new_client_connection<LaunchServer::ClientConnection>(client_socket.release_nonnull(), client_id);
|
||||
};
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
10
Userland/Services/LookupServer/CMakeLists.txt
Normal file
10
Userland/Services/LookupServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
set(SOURCES
|
||||
DNSAnswer.cpp
|
||||
DNSRequest.cpp
|
||||
DNSResponse.cpp
|
||||
LookupServer.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
serenity_bin(LookupServer)
|
||||
target_link_libraries(LookupServer LibCore)
|
46
Userland/Services/LookupServer/DNSAnswer.cpp
Normal file
46
Userland/Services/LookupServer/DNSAnswer.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 "DNSAnswer.h"
|
||||
#include <time.h>
|
||||
|
||||
DNSAnswer::DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data)
|
||||
: m_name(name)
|
||||
, m_type(type)
|
||||
, m_class_code(class_code)
|
||||
, m_ttl(ttl)
|
||||
, m_record_data(record_data)
|
||||
{
|
||||
auto now = time(nullptr);
|
||||
m_expiration_time = now + m_ttl;
|
||||
if (m_expiration_time < now)
|
||||
m_expiration_time = 0;
|
||||
}
|
||||
|
||||
bool DNSAnswer::has_expired() const
|
||||
{
|
||||
return time(nullptr) >= m_expiration_time;
|
||||
}
|
51
Userland/Services/LookupServer/DNSAnswer.h
Normal file
51
Userland/Services/LookupServer/DNSAnswer.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
class DNSAnswer {
|
||||
public:
|
||||
DNSAnswer(const String& name, u16 type, u16 class_code, u32 ttl, const String& record_data);
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
u16 type() const { return m_type; }
|
||||
u16 class_code() const { return m_class_code; }
|
||||
u32 ttl() const { return m_ttl; }
|
||||
const String& record_data() const { return m_record_data; }
|
||||
|
||||
bool has_expired() const;
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
u16 m_type { 0 };
|
||||
u16 m_class_code { 0 };
|
||||
u32 m_ttl { 0 };
|
||||
time_t m_expiration_time { 0 };
|
||||
String m_record_data;
|
||||
};
|
115
Userland/Services/LookupServer/DNSPacket.h
Normal file
115
Userland/Services/LookupServer/DNSPacket.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Endian.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
class [[gnu::packed]] DNSPacket {
|
||||
public:
|
||||
DNSPacket()
|
||||
: m_recursion_desired(false)
|
||||
, m_truncated(false)
|
||||
, m_authoritative_answer(false)
|
||||
, m_opcode(0)
|
||||
, m_query_or_response(false)
|
||||
, m_response_code(0)
|
||||
, m_checking_disabled(false)
|
||||
, m_authenticated_data(false)
|
||||
, m_zero(false)
|
||||
, m_recursion_available(false)
|
||||
{
|
||||
}
|
||||
|
||||
u16 id() const { return m_id; }
|
||||
void set_id(u16 w) { m_id = w; }
|
||||
|
||||
bool recursion_desired() const { return m_recursion_desired; }
|
||||
void set_recursion_desired(bool b) { m_recursion_desired = b; }
|
||||
|
||||
bool is_truncated() const { return m_truncated; }
|
||||
void set_truncated(bool b) { m_truncated = b; }
|
||||
|
||||
bool is_authoritative_answer() const { return m_authoritative_answer; }
|
||||
void set_authoritative_answer(bool b) { m_authoritative_answer = b; }
|
||||
|
||||
u8 opcode() const { return m_opcode; }
|
||||
void set_opcode(u8 b) { m_opcode = b; }
|
||||
|
||||
bool is_query() const { return !m_query_or_response; }
|
||||
bool is_response() const { return m_query_or_response; }
|
||||
void set_is_query() { m_query_or_response = false; }
|
||||
void set_is_response() { m_query_or_response = true; }
|
||||
|
||||
u8 response_code() const { return m_response_code; }
|
||||
void set_response_code(u8 b) { m_response_code = b; }
|
||||
|
||||
bool checking_disabled() const { return m_checking_disabled; }
|
||||
void set_checking_disabled(bool b) { m_checking_disabled = b; }
|
||||
|
||||
bool is_authenticated_data() const { return m_authenticated_data; }
|
||||
void set_authenticated_data(bool b) { m_authenticated_data = b; }
|
||||
|
||||
bool is_recursion_available() const { return m_recursion_available; }
|
||||
void set_recursion_available(bool b) { m_recursion_available = b; }
|
||||
|
||||
u16 question_count() const { return m_question_count; }
|
||||
void set_question_count(u16 w) { m_question_count = w; }
|
||||
|
||||
u16 answer_count() const { return m_answer_count; }
|
||||
void set_answer_count(u16 w) { m_answer_count = w; }
|
||||
|
||||
u16 authority_count() const { return m_authority_count; }
|
||||
void set_authority_count(u16 w) { m_authority_count = w; }
|
||||
|
||||
u16 additional_count() const { return m_additional_count; }
|
||||
void set_additional_count(u16 w) { m_additional_count = w; }
|
||||
|
||||
void* payload() { return this + 1; }
|
||||
const void* payload() const { return this + 1; }
|
||||
|
||||
private:
|
||||
NetworkOrdered<u16> m_id;
|
||||
|
||||
bool m_recursion_desired : 1;
|
||||
bool m_truncated : 1;
|
||||
bool m_authoritative_answer : 1;
|
||||
u8 m_opcode : 4;
|
||||
bool m_query_or_response : 1;
|
||||
u8 m_response_code : 4;
|
||||
bool m_checking_disabled : 1;
|
||||
bool m_authenticated_data : 1;
|
||||
bool m_zero : 1;
|
||||
bool m_recursion_available : 1;
|
||||
|
||||
NetworkOrdered<u16> m_question_count;
|
||||
NetworkOrdered<u16> m_answer_count;
|
||||
NetworkOrdered<u16> m_authority_count;
|
||||
NetworkOrdered<u16> m_additional_count;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DNSPacket) == 12);
|
59
Userland/Services/LookupServer/DNSQuestion.h
Normal file
59
Userland/Services/LookupServer/DNSQuestion.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <AK/Types.h>
|
||||
|
||||
class DNSQuestion {
|
||||
public:
|
||||
DNSQuestion(const String& name, u16 record_type, u16 class_code)
|
||||
: m_name(name)
|
||||
, m_record_type(record_type)
|
||||
, m_class_code(class_code)
|
||||
{
|
||||
}
|
||||
|
||||
u16 record_type() const { return m_record_type; }
|
||||
u16 class_code() const { return m_class_code; }
|
||||
const String& name() const { return m_name; }
|
||||
|
||||
bool operator==(const DNSQuestion& other) const
|
||||
{
|
||||
return m_name == other.m_name && m_record_type == other.m_record_type && m_class_code == other.m_class_code;
|
||||
}
|
||||
|
||||
bool operator!=(const DNSQuestion& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
u16 m_record_type { 0 };
|
||||
u16 m_class_code { 0 };
|
||||
};
|
96
Userland/Services/LookupServer/DNSRequest.cpp
Normal file
96
Userland/Services/LookupServer/DNSRequest.cpp
Normal 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 "DNSRequest.h"
|
||||
#include "DNSPacket.h"
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
const u16 C_IN = 1;
|
||||
|
||||
DNSRequest::DNSRequest()
|
||||
: m_id(arc4random_uniform(UINT16_MAX))
|
||||
{
|
||||
}
|
||||
|
||||
void DNSRequest::add_question(const String& name, u16 record_type, ShouldRandomizeCase should_randomize_case)
|
||||
{
|
||||
ASSERT(m_questions.size() <= UINT16_MAX);
|
||||
|
||||
if (name.is_empty())
|
||||
return;
|
||||
|
||||
StringBuilder builder;
|
||||
for (size_t i = 0; i < name.length(); ++i) {
|
||||
u8 ch = name[i];
|
||||
if (should_randomize_case == ShouldRandomizeCase::Yes) {
|
||||
// Randomize the 0x20 bit in every ASCII character.
|
||||
if (isalpha(ch)) {
|
||||
if (arc4random_uniform(2))
|
||||
ch |= 0x20;
|
||||
else
|
||||
ch &= ~0x20;
|
||||
}
|
||||
}
|
||||
builder.append(ch);
|
||||
}
|
||||
|
||||
if (name[name.length() - 1] != '.')
|
||||
builder.append('.');
|
||||
|
||||
m_questions.empend(builder.to_string(), record_type, C_IN);
|
||||
}
|
||||
|
||||
ByteBuffer DNSRequest::to_byte_buffer() const
|
||||
{
|
||||
DNSPacket request_header;
|
||||
request_header.set_id(m_id);
|
||||
request_header.set_is_query();
|
||||
request_header.set_opcode(0);
|
||||
request_header.set_truncated(false);
|
||||
request_header.set_recursion_desired(true);
|
||||
request_header.set_question_count(m_questions.size());
|
||||
|
||||
DuplexMemoryStream stream;
|
||||
|
||||
stream << ReadonlyBytes { &request_header, sizeof(request_header) };
|
||||
|
||||
for (auto& question : m_questions) {
|
||||
auto parts = question.name().split('.');
|
||||
for (auto& part : parts) {
|
||||
stream << (u8)part.length();
|
||||
stream << part.bytes();
|
||||
}
|
||||
stream << '\0';
|
||||
stream << htons(question.record_type());
|
||||
stream << htons(question.class_code());
|
||||
}
|
||||
|
||||
return stream.copy_into_contiguous_buffer();
|
||||
}
|
65
Userland/Services/LookupServer/DNSRequest.h
Normal file
65
Userland/Services/LookupServer/DNSRequest.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSQuestion.h"
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
#define T_A 1
|
||||
#define T_NS 2
|
||||
#define T_CNAME 5
|
||||
#define T_SOA 6
|
||||
#define T_PTR 12
|
||||
#define T_MX 15
|
||||
|
||||
enum class ShouldRandomizeCase {
|
||||
No = 0,
|
||||
Yes
|
||||
};
|
||||
|
||||
class DNSRequest {
|
||||
public:
|
||||
DNSRequest();
|
||||
|
||||
void add_question(const String& name, u16 record_type, ShouldRandomizeCase);
|
||||
|
||||
const Vector<DNSQuestion>& questions() const { return m_questions; }
|
||||
|
||||
u16 question_count() const
|
||||
{
|
||||
ASSERT(m_questions.size() < UINT16_MAX);
|
||||
return m_questions.size();
|
||||
}
|
||||
|
||||
u16 id() const { return m_id; }
|
||||
ByteBuffer to_byte_buffer() const;
|
||||
|
||||
private:
|
||||
u16 m_id { 0 };
|
||||
Vector<DNSQuestion> m_questions;
|
||||
};
|
152
Userland/Services/LookupServer/DNSResponse.cpp
Normal file
152
Userland/Services/LookupServer/DNSResponse.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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 "DNSResponse.h"
|
||||
#include "DNSPacket.h"
|
||||
#include "DNSRequest.h"
|
||||
#include <AK/IPv4Address.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
static String parse_dns_name(const u8* data, size_t& offset, size_t max_offset, size_t recursion_level = 0);
|
||||
|
||||
class [[gnu::packed]] DNSRecordWithoutName {
|
||||
public:
|
||||
DNSRecordWithoutName() { }
|
||||
|
||||
u16 type() const { return m_type; }
|
||||
u16 record_class() const { return m_class; }
|
||||
u32 ttl() const { return m_ttl; }
|
||||
u16 data_length() const { return m_data_length; }
|
||||
|
||||
void* data() { return this + 1; }
|
||||
const void* data() const { return this + 1; }
|
||||
|
||||
private:
|
||||
NetworkOrdered<u16> m_type;
|
||||
NetworkOrdered<u16> m_class;
|
||||
NetworkOrdered<u32> m_ttl;
|
||||
NetworkOrdered<u16> m_data_length;
|
||||
};
|
||||
|
||||
static_assert(sizeof(DNSRecordWithoutName) == 10);
|
||||
|
||||
Optional<DNSResponse> DNSResponse::from_raw_response(const u8* raw_data, size_t raw_size)
|
||||
{
|
||||
if (raw_size < sizeof(DNSPacket)) {
|
||||
dbgln("DNS response not large enough ({} out of {}) to be a DNS packet.", raw_size, sizeof(DNSPacket));
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& response_header = *(const DNSPacket*)(raw_data);
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
dbgln("Got response (ID: {})", response_header.id());
|
||||
dbgln(" Question count: {}", response_header.question_count());
|
||||
dbgln(" Answer count: {}", response_header.answer_count());
|
||||
dbgln(" Authority count: {}", response_header.authority_count());
|
||||
dbgln("Additional count: {}", response_header.additional_count());
|
||||
#endif
|
||||
|
||||
DNSResponse response;
|
||||
response.m_id = response_header.id();
|
||||
response.m_code = response_header.response_code();
|
||||
|
||||
if (response.code() != DNSResponse::Code::NOERROR)
|
||||
return response;
|
||||
|
||||
size_t offset = sizeof(DNSPacket);
|
||||
|
||||
for (u16 i = 0; i < response_header.question_count(); ++i) {
|
||||
auto name = parse_dns_name(raw_data, offset, raw_size);
|
||||
struct RawDNSAnswerQuestion {
|
||||
NetworkOrdered<u16> record_type;
|
||||
NetworkOrdered<u16> class_code;
|
||||
};
|
||||
auto& record_and_class = *(const RawDNSAnswerQuestion*)&raw_data[offset];
|
||||
response.m_questions.empend(name, record_and_class.record_type, record_and_class.class_code);
|
||||
offset += 4;
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
auto& question = response.m_questions.last();
|
||||
dbgln("Question #{}: name=_{}_, type={}, class={}", i, question.name(), question.record_type(), question.class_code());
|
||||
#endif
|
||||
}
|
||||
|
||||
for (u16 i = 0; i < response_header.answer_count(); ++i) {
|
||||
auto name = parse_dns_name(raw_data, offset, raw_size);
|
||||
|
||||
auto& record = *(const DNSRecordWithoutName*)(&raw_data[offset]);
|
||||
|
||||
String data;
|
||||
|
||||
offset += sizeof(DNSRecordWithoutName);
|
||||
if (record.type() == T_PTR) {
|
||||
size_t dummy_offset = offset;
|
||||
data = parse_dns_name(raw_data, dummy_offset, raw_size);
|
||||
} else if (record.type() == T_A) {
|
||||
auto ipv4_address = IPv4Address((const u8*)record.data());
|
||||
data = ipv4_address.to_string();
|
||||
} else {
|
||||
// FIXME: Parse some other record types perhaps?
|
||||
dbgln("data=(unimplemented record type {})", record.type());
|
||||
}
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
dbgln("Answer #{}: name=_{}_, type={}, ttl={}, length={}, data=_{}_", i, name, record.type(), record.ttl(), record.data_length(), data);
|
||||
#endif
|
||||
response.m_answers.empend(name, record.type(), record.record_class(), record.ttl(), data);
|
||||
offset += record.data_length();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
String parse_dns_name(const u8* data, size_t& offset, size_t max_offset, size_t recursion_level)
|
||||
{
|
||||
if (recursion_level > 4)
|
||||
return {};
|
||||
Vector<char, 128> buf;
|
||||
while (offset < max_offset) {
|
||||
u8 ch = data[offset];
|
||||
if (ch == '\0') {
|
||||
++offset;
|
||||
break;
|
||||
}
|
||||
if ((ch & 0xc0) == 0xc0) {
|
||||
if ((offset + 1) >= max_offset)
|
||||
return {};
|
||||
size_t dummy = (ch & 0x3f) << 8 | data[offset + 1];
|
||||
offset += 2;
|
||||
StringBuilder builder;
|
||||
builder.append(buf.data(), buf.size());
|
||||
auto okay = parse_dns_name(data, dummy, max_offset, recursion_level + 1);
|
||||
builder.append(okay);
|
||||
return builder.to_string();
|
||||
}
|
||||
for (size_t i = 0; i < ch; ++i)
|
||||
buf.append(data[offset + i + 1]);
|
||||
buf.append('.');
|
||||
offset += ch + 1;
|
||||
}
|
||||
return String::copy(buf);
|
||||
}
|
77
Userland/Services/LookupServer/DNSResponse.h
Normal file
77
Userland/Services/LookupServer/DNSResponse.h
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSAnswer.h"
|
||||
#include "DNSQuestion.h"
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
|
||||
class DNSResponse {
|
||||
public:
|
||||
static Optional<DNSResponse> from_raw_response(const u8*, size_t);
|
||||
|
||||
u16 id() const { return m_id; }
|
||||
const Vector<DNSQuestion>& questions() const { return m_questions; }
|
||||
const Vector<DNSAnswer>& answers() const { return m_answers; }
|
||||
|
||||
u16 question_count() const
|
||||
{
|
||||
ASSERT(m_questions.size() <= UINT16_MAX);
|
||||
return m_questions.size();
|
||||
}
|
||||
|
||||
u16 answer_count() const
|
||||
{
|
||||
ASSERT(m_answers.size() <= UINT16_MAX);
|
||||
return m_answers.size();
|
||||
}
|
||||
|
||||
enum class Code : u8 {
|
||||
NOERROR = 0,
|
||||
FORMERR = 1,
|
||||
SERVFAIL = 2,
|
||||
NXDOMAIN = 3,
|
||||
NOTIMP = 4,
|
||||
REFUSED = 5,
|
||||
YXDOMAIN = 6,
|
||||
XRRSET = 7,
|
||||
NOTAUTH = 8,
|
||||
NOTZONE = 9,
|
||||
};
|
||||
|
||||
Code code() const { return (Code)m_code; }
|
||||
|
||||
private:
|
||||
DNSResponse() { }
|
||||
|
||||
u16 m_id { 0 };
|
||||
u8 m_code { 0 };
|
||||
Vector<DNSQuestion> m_questions;
|
||||
Vector<DNSAnswer> m_answers;
|
||||
};
|
278
Userland/Services/LookupServer/LookupServer.cpp
Normal file
278
Userland/Services/LookupServer/LookupServer.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* 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 "LookupServer.h"
|
||||
#include "DNSRequest.h"
|
||||
#include "DNSResponse.h"
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibCore/LocalSocket.h>
|
||||
#include <LibCore/UDPSocket.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//#define LOOKUPSERVER_DEBUG
|
||||
|
||||
LookupServer::LookupServer()
|
||||
{
|
||||
auto config = Core::ConfigFile::get_for_system("LookupServer");
|
||||
dbgln("Using network config file at {}", config->file_name());
|
||||
m_nameservers = config->read_entry("DNS", "Nameservers", "1.1.1.1,1.0.0.1").split(',');
|
||||
|
||||
load_etc_hosts();
|
||||
|
||||
m_local_server = Core::LocalServer::construct(this);
|
||||
m_local_server->on_ready_to_accept = [this]() {
|
||||
auto socket = m_local_server->accept();
|
||||
socket->on_ready_to_read = [this, socket]() {
|
||||
service_client(socket);
|
||||
RefPtr<Core::LocalSocket> keeper = socket;
|
||||
const_cast<Core::LocalSocket&>(*socket).on_ready_to_read = [] {};
|
||||
};
|
||||
};
|
||||
bool ok = m_local_server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
}
|
||||
|
||||
void LookupServer::load_etc_hosts()
|
||||
{
|
||||
auto file = Core::File::construct("/etc/hosts");
|
||||
if (!file->open(Core::IODevice::ReadOnly))
|
||||
return;
|
||||
while (!file->eof()) {
|
||||
auto line = file->read_line(1024);
|
||||
if (line.is_empty())
|
||||
break;
|
||||
auto fields = line.split('\t');
|
||||
|
||||
auto sections = fields[0].split('.');
|
||||
IPv4Address addr {
|
||||
(u8)atoi(sections[0].characters()),
|
||||
(u8)atoi(sections[1].characters()),
|
||||
(u8)atoi(sections[2].characters()),
|
||||
(u8)atoi(sections[3].characters()),
|
||||
};
|
||||
|
||||
auto name = fields[1];
|
||||
m_etc_hosts.set(name, addr.to_string());
|
||||
|
||||
IPv4Address reverse_addr {
|
||||
(u8)atoi(sections[3].characters()),
|
||||
(u8)atoi(sections[2].characters()),
|
||||
(u8)atoi(sections[1].characters()),
|
||||
(u8)atoi(sections[0].characters()),
|
||||
};
|
||||
StringBuilder builder;
|
||||
builder.append(reverse_addr.to_string());
|
||||
builder.append(".in-addr.arpa");
|
||||
m_etc_hosts.set(builder.to_string(), name);
|
||||
}
|
||||
}
|
||||
|
||||
void LookupServer::service_client(RefPtr<Core::LocalSocket> socket)
|
||||
{
|
||||
u8 client_buffer[1024];
|
||||
int nrecv = socket->read(client_buffer, sizeof(client_buffer) - 1);
|
||||
if (nrecv < 0) {
|
||||
perror("read");
|
||||
return;
|
||||
}
|
||||
|
||||
client_buffer[nrecv] = '\0';
|
||||
|
||||
char lookup_type = client_buffer[0];
|
||||
if (lookup_type != 'L' && lookup_type != 'R') {
|
||||
dbgln("Invalid lookup_type '{}'", lookup_type);
|
||||
return;
|
||||
}
|
||||
auto hostname = String((const char*)client_buffer + 1, nrecv - 1, Chomp);
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
dbgln("Got request for '{}'", hostname);
|
||||
#endif
|
||||
|
||||
Vector<String> responses;
|
||||
|
||||
if (auto known_host = m_etc_hosts.get(hostname); known_host.has_value()) {
|
||||
responses.append(known_host.value());
|
||||
} else if (!hostname.is_empty()) {
|
||||
for (auto& nameserver : m_nameservers) {
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
dbgln("Doing lookup using nameserver '{}'", nameserver);
|
||||
#endif
|
||||
bool did_get_response = false;
|
||||
int retries = 3;
|
||||
do {
|
||||
if (lookup_type == 'L')
|
||||
responses = lookup(hostname, nameserver, did_get_response, T_A);
|
||||
else if (lookup_type == 'R')
|
||||
responses = lookup(hostname, nameserver, did_get_response, T_PTR);
|
||||
if (did_get_response)
|
||||
break;
|
||||
} while (--retries);
|
||||
if (!responses.is_empty()) {
|
||||
break;
|
||||
} else {
|
||||
if (!did_get_response)
|
||||
dbgln("Never got a response from '{}', trying next nameserver", nameserver);
|
||||
else
|
||||
dbgln("Received response from '{}' but no result(s), trying next nameserver", nameserver);
|
||||
}
|
||||
}
|
||||
if (responses.is_empty()) {
|
||||
fprintf(stderr, "LookupServer: Tried all nameservers but never got a response :(\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (responses.is_empty()) {
|
||||
int nsent = socket->write("Not found.\n");
|
||||
if (nsent < 0)
|
||||
perror("write");
|
||||
return;
|
||||
}
|
||||
for (auto& response : responses) {
|
||||
auto line = String::format("%s\n", response.characters());
|
||||
int nsent = socket->write(line);
|
||||
if (nsent < 0) {
|
||||
perror("write");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<String> LookupServer::lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase should_randomize_case)
|
||||
{
|
||||
if (auto it = m_lookup_cache.find(hostname); it != m_lookup_cache.end()) {
|
||||
auto& cached_lookup = it->value;
|
||||
if (cached_lookup.question.record_type() == record_type) {
|
||||
Vector<String> responses;
|
||||
for (auto& cached_answer : cached_lookup.answers) {
|
||||
#ifdef LOOKUPSERVER_DEBUG
|
||||
dbgln("Cache hit: {} -> {}, expired: {}", hostname, cached_answer.record_data(), cached_answer.has_expired());
|
||||
#endif
|
||||
if (!cached_answer.has_expired())
|
||||
responses.append(cached_answer.record_data());
|
||||
}
|
||||
if (!responses.is_empty())
|
||||
return responses;
|
||||
}
|
||||
m_lookup_cache.remove(it);
|
||||
}
|
||||
|
||||
DNSRequest request;
|
||||
request.add_question(hostname, record_type, should_randomize_case);
|
||||
|
||||
auto buffer = request.to_byte_buffer();
|
||||
|
||||
auto udp_socket = Core::UDPSocket::construct();
|
||||
udp_socket->set_blocking(true);
|
||||
|
||||
struct timeval timeout {
|
||||
1, 0
|
||||
};
|
||||
|
||||
int rc = setsockopt(udp_socket->fd(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||
if (rc < 0) {
|
||||
perror("setsockopt(SOL_SOCKET, SO_RCVTIMEO)");
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!udp_socket->connect(nameserver, 53))
|
||||
return {};
|
||||
|
||||
if (!udp_socket->write(buffer))
|
||||
return {};
|
||||
|
||||
u8 response_buffer[4096];
|
||||
int nrecv = udp_socket->read(response_buffer, sizeof(response_buffer));
|
||||
if (nrecv == 0)
|
||||
return {};
|
||||
|
||||
did_get_response = true;
|
||||
|
||||
auto o_response = DNSResponse::from_raw_response(response_buffer, nrecv);
|
||||
if (!o_response.has_value())
|
||||
return {};
|
||||
|
||||
auto& response = o_response.value();
|
||||
|
||||
if (response.id() != request.id()) {
|
||||
dbgln("LookupServer: ID mismatch ({} vs {}) :(", response.id(), request.id());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (response.code() == DNSResponse::Code::REFUSED) {
|
||||
if (should_randomize_case == ShouldRandomizeCase::Yes) {
|
||||
// Retry with 0x20 case randomization turned off.
|
||||
return lookup(hostname, nameserver, did_get_response, record_type, ShouldRandomizeCase::No);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
if (response.question_count() != request.question_count()) {
|
||||
dbgln("LookupServer: Question count ({} vs {}) :(", response.question_count(), request.question_count());
|
||||
return {};
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < request.question_count(); ++i) {
|
||||
auto& request_question = request.questions()[i];
|
||||
auto& response_question = response.questions()[i];
|
||||
if (request_question != response_question) {
|
||||
dbgln("Request and response questions do not match");
|
||||
dbgln(" Request: name=_{}_, type={}, class={}", request_question.name(), response_question.record_type(), response_question.class_code());
|
||||
dbgln(" Response: name=_{}_, type={}, class={}", response_question.name(), response_question.record_type(), response_question.class_code());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (response.answer_count() < 1) {
|
||||
dbgln("LookupServer: Not enough answers ({}) :(", response.answer_count());
|
||||
return {};
|
||||
}
|
||||
|
||||
Vector<String, 8> responses;
|
||||
Vector<DNSAnswer, 8> cacheable_answers;
|
||||
for (auto& answer : response.answers()) {
|
||||
if (answer.type() != T_A)
|
||||
continue;
|
||||
responses.append(answer.record_data());
|
||||
if (!answer.has_expired())
|
||||
cacheable_answers.append(answer);
|
||||
}
|
||||
|
||||
if (!cacheable_answers.is_empty()) {
|
||||
if (m_lookup_cache.size() >= 256)
|
||||
m_lookup_cache.remove(m_lookup_cache.begin());
|
||||
m_lookup_cache.set(hostname, { request.questions()[0], move(cacheable_answers) });
|
||||
}
|
||||
return responses;
|
||||
}
|
56
Userland/Services/LookupServer/LookupServer.h
Normal file
56
Userland/Services/LookupServer/LookupServer.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNSRequest.h"
|
||||
#include "DNSResponse.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibCore/Object.h>
|
||||
|
||||
class DNSAnswer;
|
||||
|
||||
class LookupServer final : public Core::Object {
|
||||
C_OBJECT(LookupServer)
|
||||
|
||||
public:
|
||||
LookupServer();
|
||||
|
||||
private:
|
||||
void load_etc_hosts();
|
||||
void service_client(RefPtr<Core::LocalSocket>);
|
||||
Vector<String> lookup(const String& hostname, const String& nameserver, bool& did_get_response, unsigned short record_type, ShouldRandomizeCase = ShouldRandomizeCase::Yes);
|
||||
|
||||
struct CachedLookup {
|
||||
DNSQuestion question;
|
||||
Vector<DNSAnswer> answers;
|
||||
};
|
||||
|
||||
RefPtr<Core::LocalServer> m_local_server;
|
||||
Vector<String> m_nameservers;
|
||||
HashMap<String, String> m_etc_hosts;
|
||||
HashMap<String, CachedLookup> m_lookup_cache;
|
||||
};
|
50
Userland/Services/LookupServer/main.cpp
Normal file
50
Userland/Services/LookupServer/main.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 "LookupServer.h"
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
|
||||
{
|
||||
if (pledge("stdio accept unix inet cpath rpath fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
LookupServer server;
|
||||
|
||||
if (pledge("stdio accept inet", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
return event_loop.exec();
|
||||
}
|
13
Userland/Services/NotificationServer/CMakeLists.txt
Normal file
13
Userland/Services/NotificationServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,13 @@
|
|||
compile_ipc(NotificationServer.ipc NotificationServerEndpoint.h)
|
||||
compile_ipc(NotificationClient.ipc NotificationClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
main.cpp
|
||||
NotificationWindow.cpp
|
||||
NotificationServerEndpoint.h
|
||||
NotificationClientEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(NotificationServer)
|
||||
target_link_libraries(NotificationServer LibGUI LibIPC)
|
63
Userland/Services/NotificationServer/ClientConnection.cpp
Normal file
63
Userland/Services/NotificationServer/ClientConnection.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 "ClientConnection.h"
|
||||
#include "NotificationWindow.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <NotificationServer/NotificationClientEndpoint.h>
|
||||
|
||||
namespace NotificationServer {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> client_socket, int client_id)
|
||||
: IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint>(*this, move(client_socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::NotificationServer::GreetResponse> ClientConnection::handle(const Messages::NotificationServer::Greet&)
|
||||
{
|
||||
return make<Messages::NotificationServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::NotificationServer::ShowNotificationResponse> ClientConnection::handle(const Messages::NotificationServer::ShowNotification& message)
|
||||
{
|
||||
auto window = NotificationWindow::construct(message.text(), message.title(), message.icon());
|
||||
window->show();
|
||||
return make<Messages::NotificationServer::ShowNotificationResponse>();
|
||||
}
|
||||
|
||||
}
|
50
Userland/Services/NotificationServer/ClientConnection.h
Normal file
50
Userland/Services/NotificationServer/ClientConnection.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
#include <NotificationServer/NotificationClientEndpoint.h>
|
||||
#include <NotificationServer/NotificationServerEndpoint.h>
|
||||
|
||||
namespace NotificationServer {
|
||||
|
||||
class ClientConnection final : public IPC::ClientConnection<NotificationClientEndpoint, NotificationServerEndpoint>
|
||||
, public NotificationServerEndpoint {
|
||||
C_OBJECT(ClientConnection)
|
||||
public:
|
||||
~ClientConnection() override;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
private:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
|
||||
|
||||
virtual OwnPtr<Messages::NotificationServer::GreetResponse> handle(const Messages::NotificationServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::NotificationServer::ShowNotificationResponse> handle(const Messages::NotificationServer::ShowNotification&) override;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
endpoint NotificationClient = 92
|
||||
{
|
||||
Dummy() =|
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
endpoint NotificationServer = 95
|
||||
{
|
||||
// Basic protocol
|
||||
Greet() => (i32 client_id)
|
||||
|
||||
ShowNotification([UTF8] String text, [UTF8] String title, Gfx::ShareableBitmap icon) => ()
|
||||
}
|
123
Userland/Services/NotificationServer/NotificationWindow.cpp
Normal file
123
Userland/Services/NotificationServer/NotificationWindow.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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 "NotificationWindow.h"
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Button.h>
|
||||
#include <LibGUI/Desktop.h>
|
||||
#include <LibGUI/ImageWidget.h>
|
||||
#include <LibGUI/Label.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
#include <LibGfx/ShareableBitmap.h>
|
||||
|
||||
namespace NotificationServer {
|
||||
|
||||
static Vector<RefPtr<NotificationWindow>> s_windows;
|
||||
|
||||
void update_notification_window_locations()
|
||||
{
|
||||
Gfx::IntRect last_window_rect;
|
||||
for (auto& window : s_windows) {
|
||||
Gfx::IntPoint new_window_location;
|
||||
if (last_window_rect.is_null())
|
||||
new_window_location = GUI::Desktop::the().rect().top_right().translated(-window->rect().width() - 24, 26);
|
||||
else
|
||||
new_window_location = last_window_rect.bottom_left().translated(0, 10);
|
||||
if (window->rect().location() != new_window_location) {
|
||||
window->move_to(new_window_location);
|
||||
window->set_original_rect(window->rect());
|
||||
}
|
||||
last_window_rect = window->rect();
|
||||
}
|
||||
}
|
||||
|
||||
NotificationWindow::NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap& icon)
|
||||
{
|
||||
s_windows.append(this);
|
||||
|
||||
set_window_type(GUI::WindowType::Notification);
|
||||
set_resizable(false);
|
||||
set_minimizable(false);
|
||||
|
||||
Gfx::IntRect lowest_notification_rect_on_screen;
|
||||
for (auto& window : s_windows) {
|
||||
if (window->m_original_rect.y() > lowest_notification_rect_on_screen.y())
|
||||
lowest_notification_rect_on_screen = window->m_original_rect;
|
||||
}
|
||||
|
||||
Gfx::IntRect rect;
|
||||
rect.set_width(220);
|
||||
rect.set_height(40);
|
||||
rect.set_location(GUI::Desktop::the().rect().top_right().translated(-rect.width() - 24, 26));
|
||||
|
||||
if (!lowest_notification_rect_on_screen.is_null())
|
||||
rect.set_location(lowest_notification_rect_on_screen.bottom_left().translated(0, 10));
|
||||
|
||||
set_rect(rect);
|
||||
|
||||
m_original_rect = rect;
|
||||
|
||||
auto& widget = set_main_widget<GUI::Widget>();
|
||||
widget.set_fill_with_background_color(true);
|
||||
|
||||
widget.set_layout<GUI::HorizontalBoxLayout>();
|
||||
widget.layout()->set_margins({ 8, 8, 8, 8 });
|
||||
widget.layout()->set_spacing(6);
|
||||
|
||||
if (icon.is_valid()) {
|
||||
auto& image = widget.add<GUI::ImageWidget>();
|
||||
image.set_bitmap(icon.bitmap());
|
||||
}
|
||||
|
||||
auto& left_container = widget.add<GUI::Widget>();
|
||||
left_container.set_layout<GUI::VerticalBoxLayout>();
|
||||
|
||||
auto& title_label = left_container.add<GUI::Label>(title);
|
||||
title_label.set_font(Gfx::FontDatabase::default_bold_font());
|
||||
title_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||
auto& text_label = left_container.add<GUI::Label>(text);
|
||||
text_label.set_text_alignment(Gfx::TextAlignment::CenterLeft);
|
||||
|
||||
auto& right_container = widget.add<GUI::Widget>();
|
||||
right_container.set_fixed_width(36);
|
||||
right_container.set_layout<GUI::HorizontalBoxLayout>();
|
||||
|
||||
on_close_request = [this] {
|
||||
s_windows.remove_first_matching([this](auto& entry) { return entry == this; });
|
||||
update_notification_window_locations();
|
||||
return CloseRequestDecision::Close;
|
||||
};
|
||||
}
|
||||
|
||||
NotificationWindow::~NotificationWindow()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
48
Userland/Services/NotificationServer/NotificationWindow.h
Normal file
48
Userland/Services/NotificationServer/NotificationWindow.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
namespace NotificationServer {
|
||||
|
||||
void update_notification_window_locations();
|
||||
|
||||
class NotificationWindow final : public GUI::Window {
|
||||
C_OBJECT(NotificationWindow);
|
||||
|
||||
public:
|
||||
virtual ~NotificationWindow() override;
|
||||
void set_original_rect(Gfx::IntRect original_rect) { m_original_rect = original_rect; };
|
||||
|
||||
private:
|
||||
NotificationWindow(const String& text, const String& title, const Gfx::ShareableBitmap&);
|
||||
|
||||
Gfx::IntRect m_original_rect;
|
||||
};
|
||||
|
||||
}
|
74
Userland/Services/NotificationServer/main.cpp
Normal file
74
Userland/Services/NotificationServer/main.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 "ClientConnection.h"
|
||||
#include "NotificationWindow.h"
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/Desktop.h>
|
||||
#include <LibGUI/WindowServerConnection.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
if (pledge("stdio shared_buffer accept rpath wpath cpath unix fattr", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
auto server = Core::LocalServer::construct();
|
||||
|
||||
bool ok = server->take_over_from_system_server();
|
||||
ASSERT(ok);
|
||||
server->on_ready_to_accept = [&] {
|
||||
auto client_socket = server->accept();
|
||||
if (!client_socket) {
|
||||
dbgln("NotificationServer: accept failed.");
|
||||
return;
|
||||
}
|
||||
static int s_next_client_id = 0;
|
||||
int client_id = ++s_next_client_id;
|
||||
IPC::new_client_connection<NotificationServer::ClientConnection>(client_socket.release_nonnull(), client_id);
|
||||
};
|
||||
|
||||
if (unveil("/res", "r") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
if (pledge("stdio shared_buffer accept rpath", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
GUI::Desktop::the().on_rect_change = [](auto&) { NotificationServer::update_notification_window_locations(); };
|
||||
|
||||
return app->exec();
|
||||
}
|
20
Userland/Services/ProtocolServer/CMakeLists.txt
Normal file
20
Userland/Services/ProtocolServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
compile_ipc(ProtocolServer.ipc ProtocolServerEndpoint.h)
|
||||
compile_ipc(ProtocolClient.ipc ProtocolClientEndpoint.h)
|
||||
|
||||
set(SOURCES
|
||||
ClientConnection.cpp
|
||||
Download.cpp
|
||||
GeminiDownload.cpp
|
||||
GeminiProtocol.cpp
|
||||
HttpDownload.cpp
|
||||
HttpProtocol.cpp
|
||||
HttpsDownload.cpp
|
||||
HttpsProtocol.cpp
|
||||
main.cpp
|
||||
Protocol.cpp
|
||||
ProtocolServerEndpoint.h
|
||||
ProtocolClientEndpoint.h
|
||||
)
|
||||
|
||||
serenity_bin(ProtocolServer)
|
||||
target_link_libraries(ProtocolServer LibCore LibIPC LibGemini LibHTTP)
|
142
Userland/Services/ProtocolServer/ClientConnection.cpp
Normal file
142
Userland/Services/ProtocolServer/ClientConnection.cpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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/Badge.h>
|
||||
#include <AK/SharedBuffer.h>
|
||||
#include <ProtocolServer/ClientConnection.h>
|
||||
#include <ProtocolServer/Download.h>
|
||||
#include <ProtocolServer/Protocol.h>
|
||||
#include <ProtocolServer/ProtocolClientEndpoint.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
static HashMap<int, RefPtr<ClientConnection>> s_connections;
|
||||
|
||||
ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id)
|
||||
: IPC::ClientConnection<ProtocolClientEndpoint, ProtocolServerEndpoint>(*this, move(socket), client_id)
|
||||
{
|
||||
s_connections.set(client_id, *this);
|
||||
}
|
||||
|
||||
ClientConnection::~ClientConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void ClientConnection::die()
|
||||
{
|
||||
s_connections.remove(client_id());
|
||||
if (s_connections.is_empty())
|
||||
Core::EventLoop::current().quit(0);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ProtocolServer::IsSupportedProtocolResponse> ClientConnection::handle(const Messages::ProtocolServer::IsSupportedProtocol& message)
|
||||
{
|
||||
bool supported = Protocol::find_by_name(message.protocol().to_lowercase());
|
||||
return make<Messages::ProtocolServer::IsSupportedProtocolResponse>(supported);
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ProtocolServer::StartDownloadResponse> ClientConnection::handle(const Messages::ProtocolServer::StartDownload& message)
|
||||
{
|
||||
const auto& url = message.url();
|
||||
if (!url.is_valid()) {
|
||||
dbgln("StartDownload: Invalid URL requested: '{}'", url);
|
||||
return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {});
|
||||
}
|
||||
auto* protocol = Protocol::find_by_name(url.protocol());
|
||||
if (!protocol) {
|
||||
dbgln("StartDownload: No protocol handler for URL: '{}'", url);
|
||||
return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {});
|
||||
}
|
||||
auto download = protocol->start_download(*this, message.method(), url, message.request_headers().entries(), message.request_body());
|
||||
if (!download) {
|
||||
dbgln("StartDownload: Protocol handler failed to start download: '{}'", url);
|
||||
return make<Messages::ProtocolServer::StartDownloadResponse>(-1, Optional<IPC::File> {});
|
||||
}
|
||||
auto id = download->id();
|
||||
auto fd = download->download_fd();
|
||||
m_downloads.set(id, move(download));
|
||||
auto response = make<Messages::ProtocolServer::StartDownloadResponse>(id, fd);
|
||||
response->on_destruction = [fd] { close(fd); };
|
||||
return response;
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ProtocolServer::StopDownloadResponse> ClientConnection::handle(const Messages::ProtocolServer::StopDownload& message)
|
||||
{
|
||||
auto* download = const_cast<Download*>(m_downloads.get(message.download_id()).value_or(nullptr));
|
||||
bool success = false;
|
||||
if (download) {
|
||||
download->stop();
|
||||
m_downloads.remove(message.download_id());
|
||||
success = true;
|
||||
}
|
||||
return make<Messages::ProtocolServer::StopDownloadResponse>(success);
|
||||
}
|
||||
|
||||
void ClientConnection::did_receive_headers(Badge<Download>, Download& download)
|
||||
{
|
||||
IPC::Dictionary response_headers;
|
||||
for (auto& it : download.response_headers())
|
||||
response_headers.add(it.key, it.value);
|
||||
|
||||
post_message(Messages::ProtocolClient::HeadersBecameAvailable(download.id(), move(response_headers), download.status_code()));
|
||||
}
|
||||
|
||||
void ClientConnection::did_finish_download(Badge<Download>, Download& download, bool success)
|
||||
{
|
||||
ASSERT(download.total_size().has_value());
|
||||
|
||||
post_message(Messages::ProtocolClient::DownloadFinished(download.id(), success, download.total_size().value()));
|
||||
|
||||
m_downloads.remove(download.id());
|
||||
}
|
||||
|
||||
void ClientConnection::did_progress_download(Badge<Download>, Download& download)
|
||||
{
|
||||
post_message(Messages::ProtocolClient::DownloadProgress(download.id(), download.total_size(), download.downloaded_size()));
|
||||
}
|
||||
|
||||
void ClientConnection::did_request_certificates(Badge<Download>, Download& download)
|
||||
{
|
||||
post_message(Messages::ProtocolClient::CertificateRequested(download.id()));
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ProtocolServer::GreetResponse> ClientConnection::handle(const Messages::ProtocolServer::Greet&)
|
||||
{
|
||||
return make<Messages::ProtocolServer::GreetResponse>(client_id());
|
||||
}
|
||||
|
||||
OwnPtr<Messages::ProtocolServer::SetCertificateResponse> ClientConnection::handle(const Messages::ProtocolServer::SetCertificate& message)
|
||||
{
|
||||
auto* download = const_cast<Download*>(m_downloads.get(message.download_id()).value_or(nullptr));
|
||||
bool success = false;
|
||||
if (download) {
|
||||
download->set_certificate(message.certificate(), message.key());
|
||||
success = true;
|
||||
}
|
||||
return make<Messages::ProtocolServer::SetCertificateResponse>(success);
|
||||
}
|
||||
|
||||
}
|
63
Userland/Services/ProtocolServer/ClientConnection.h
Normal file
63
Userland/Services/ProtocolServer/ClientConnection.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
#include <ProtocolServer/Forward.h>
|
||||
#include <ProtocolServer/ProtocolClientEndpoint.h>
|
||||
#include <ProtocolServer/ProtocolServerEndpoint.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class ClientConnection final
|
||||
: public IPC::ClientConnection<ProtocolClientEndpoint, ProtocolServerEndpoint>
|
||||
, public ProtocolServerEndpoint {
|
||||
C_OBJECT(ClientConnection);
|
||||
|
||||
public:
|
||||
explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id);
|
||||
~ClientConnection() override;
|
||||
|
||||
virtual void die() override;
|
||||
|
||||
void did_receive_headers(Badge<Download>, Download&);
|
||||
void did_finish_download(Badge<Download>, Download&, bool success);
|
||||
void did_progress_download(Badge<Download>, Download&);
|
||||
void did_request_certificates(Badge<Download>, Download&);
|
||||
|
||||
private:
|
||||
virtual OwnPtr<Messages::ProtocolServer::GreetResponse> handle(const Messages::ProtocolServer::Greet&) override;
|
||||
virtual OwnPtr<Messages::ProtocolServer::IsSupportedProtocolResponse> handle(const Messages::ProtocolServer::IsSupportedProtocol&) override;
|
||||
virtual OwnPtr<Messages::ProtocolServer::StartDownloadResponse> handle(const Messages::ProtocolServer::StartDownload&) override;
|
||||
virtual OwnPtr<Messages::ProtocolServer::StopDownloadResponse> handle(const Messages::ProtocolServer::StopDownload&) override;
|
||||
virtual OwnPtr<Messages::ProtocolServer::SetCertificateResponse> handle(const Messages::ProtocolServer::SetCertificate&) override;
|
||||
|
||||
HashMap<i32, OwnPtr<Download>> m_downloads;
|
||||
};
|
||||
|
||||
}
|
79
Userland/Services/ProtocolServer/Download.cpp
Normal file
79
Userland/Services/ProtocolServer/Download.cpp
Normal 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/Badge.h>
|
||||
#include <ProtocolServer/ClientConnection.h>
|
||||
#include <ProtocolServer/Download.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
// FIXME: What about rollover?
|
||||
static i32 s_next_id = 1;
|
||||
|
||||
Download::Download(ClientConnection& client, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
: m_client(client)
|
||||
, m_id(s_next_id++)
|
||||
, m_output_stream(move(output_stream))
|
||||
{
|
||||
}
|
||||
|
||||
Download::~Download()
|
||||
{
|
||||
}
|
||||
|
||||
void Download::stop()
|
||||
{
|
||||
m_client.did_finish_download({}, *this, false);
|
||||
}
|
||||
|
||||
void Download::set_response_headers(const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers)
|
||||
{
|
||||
m_response_headers = response_headers;
|
||||
m_client.did_receive_headers({}, *this);
|
||||
}
|
||||
|
||||
void Download::set_certificate(String, String)
|
||||
{
|
||||
}
|
||||
|
||||
void Download::did_finish(bool success)
|
||||
{
|
||||
m_client.did_finish_download({}, *this, success);
|
||||
}
|
||||
|
||||
void Download::did_progress(Optional<u32> total_size, u32 downloaded_size)
|
||||
{
|
||||
m_total_size = total_size;
|
||||
m_downloaded_size = downloaded_size;
|
||||
m_client.did_progress_download({}, *this);
|
||||
}
|
||||
|
||||
void Download::did_request_certificates()
|
||||
{
|
||||
m_client.did_request_certificates({}, *this);
|
||||
}
|
||||
|
||||
}
|
81
Userland/Services/ProtocolServer/Download.h
Normal file
81
Userland/Services/ProtocolServer/Download.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FileStream.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <AK/RefCounted.h>
|
||||
#include <AK/URL.h>
|
||||
#include <ProtocolServer/Forward.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class Download {
|
||||
public:
|
||||
virtual ~Download();
|
||||
|
||||
i32 id() const { return m_id; }
|
||||
URL url() const { return m_url; }
|
||||
|
||||
Optional<u32> status_code() const { return m_status_code; }
|
||||
Optional<u32> total_size() const { return m_total_size; }
|
||||
size_t downloaded_size() const { return m_downloaded_size; }
|
||||
const HashMap<String, String, CaseInsensitiveStringTraits>& response_headers() const { return m_response_headers; }
|
||||
|
||||
void stop();
|
||||
virtual void set_certificate(String, String);
|
||||
|
||||
// FIXME: Want Badge<Protocol>, but can't make one from HttpProtocol, etc.
|
||||
void set_download_fd(int fd) { m_download_fd = fd; }
|
||||
int download_fd() const { return m_download_fd; }
|
||||
|
||||
protected:
|
||||
explicit Download(ClientConnection&, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
void did_finish(bool success);
|
||||
void did_progress(Optional<u32> total_size, u32 downloaded_size);
|
||||
void set_status_code(u32 status_code) { m_status_code = status_code; }
|
||||
void did_request_certificates();
|
||||
void set_response_headers(const HashMap<String, String, CaseInsensitiveStringTraits>&);
|
||||
void set_downloaded_size(size_t size) { m_downloaded_size = size; }
|
||||
const OutputFileStream& output_stream() const { return *m_output_stream; }
|
||||
|
||||
private:
|
||||
ClientConnection& m_client;
|
||||
i32 m_id { 0 };
|
||||
int m_download_fd { -1 }; // Passed to client.
|
||||
URL m_url;
|
||||
Optional<u32> m_status_code;
|
||||
Optional<u32> m_total_size {};
|
||||
size_t m_downloaded_size { 0 };
|
||||
NonnullOwnPtr<OutputFileStream> m_output_stream;
|
||||
HashMap<String, String, CaseInsensitiveStringTraits> m_response_headers;
|
||||
};
|
||||
|
||||
}
|
40
Userland/Services/ProtocolServer/Forward.h
Normal file
40
Userland/Services/ProtocolServer/Forward.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class ClientConnection;
|
||||
class Download;
|
||||
class GeminiProtocol;
|
||||
class HttpDownload;
|
||||
class HttpProtocol;
|
||||
class HttpsDownload;
|
||||
class HttpsProtocol;
|
||||
class Protocol;
|
||||
|
||||
}
|
84
Userland/Services/ProtocolServer/GeminiDownload.cpp
Normal file
84
Userland/Services/ProtocolServer/GeminiDownload.cpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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 <LibGemini/GeminiJob.h>
|
||||
#include <LibGemini/GeminiResponse.h>
|
||||
#include <ProtocolServer/GeminiDownload.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
GeminiDownload::GeminiDownload(ClientConnection& client, NonnullRefPtr<Gemini::GeminiJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
: Download(client, move(output_stream))
|
||||
, m_job(job)
|
||||
{
|
||||
m_job->on_finish = [this](bool success) {
|
||||
if (auto* response = m_job->response()) {
|
||||
set_downloaded_size(this->output_stream().size());
|
||||
if (!response->meta().is_empty()) {
|
||||
HashMap<String, String, CaseInsensitiveStringTraits> headers;
|
||||
headers.set("meta", response->meta());
|
||||
// Note: We're setting content-type to meta only on status==SUCCESS
|
||||
// we should perhaps have a better mechanism for this, since we
|
||||
// are already shoehorning the concept of "headers" here
|
||||
if (response->status() >= 20 && response->status() < 30) {
|
||||
headers.set("content-type", response->meta());
|
||||
}
|
||||
set_response_headers(headers);
|
||||
}
|
||||
}
|
||||
|
||||
// signal 100% download progress so any listeners can react
|
||||
// appropriately
|
||||
did_progress(downloaded_size(), downloaded_size());
|
||||
|
||||
did_finish(success);
|
||||
};
|
||||
m_job->on_progress = [this](Optional<u32> total, u32 current) {
|
||||
did_progress(total, current);
|
||||
};
|
||||
m_job->on_certificate_requested = [this](auto&) {
|
||||
did_request_certificates();
|
||||
};
|
||||
}
|
||||
|
||||
void GeminiDownload::set_certificate(String certificate, String key)
|
||||
{
|
||||
m_job->set_certificate(move(certificate), move(key));
|
||||
}
|
||||
|
||||
GeminiDownload::~GeminiDownload()
|
||||
{
|
||||
m_job->on_finish = nullptr;
|
||||
m_job->on_progress = nullptr;
|
||||
m_job->shutdown();
|
||||
}
|
||||
|
||||
NonnullOwnPtr<GeminiDownload> GeminiDownload::create_with_job(Badge<GeminiProtocol>, ClientConnection& client, NonnullRefPtr<Gemini::GeminiJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
{
|
||||
return adopt_own(*new GeminiDownload(client, move(job), move(output_stream)));
|
||||
}
|
||||
|
||||
}
|
49
Userland/Services/ProtocolServer/GeminiDownload.h
Normal file
49
Userland/Services/ProtocolServer/GeminiDownload.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibGemini/Forward.h>
|
||||
#include <ProtocolServer/Download.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class GeminiDownload final : public Download {
|
||||
public:
|
||||
virtual ~GeminiDownload() override;
|
||||
static NonnullOwnPtr<GeminiDownload> create_with_job(Badge<GeminiProtocol>, ClientConnection&, NonnullRefPtr<Gemini::GeminiJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
private:
|
||||
explicit GeminiDownload(ClientConnection&, NonnullRefPtr<Gemini::GeminiJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
virtual void set_certificate(String certificate, String key) override;
|
||||
|
||||
NonnullRefPtr<Gemini::GeminiJob> m_job;
|
||||
};
|
||||
|
||||
}
|
62
Userland/Services/ProtocolServer/GeminiProtocol.cpp
Normal file
62
Userland/Services/ProtocolServer/GeminiProtocol.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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 <LibGemini/GeminiJob.h>
|
||||
#include <LibGemini/GeminiRequest.h>
|
||||
#include <ProtocolServer/GeminiDownload.h>
|
||||
#include <ProtocolServer/GeminiProtocol.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
GeminiProtocol::GeminiProtocol()
|
||||
: Protocol("gemini")
|
||||
{
|
||||
}
|
||||
|
||||
GeminiProtocol::~GeminiProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<Download> GeminiProtocol::start_download(ClientConnection& client, const String&, const URL& url, const HashMap<String, String>&, ReadonlyBytes)
|
||||
{
|
||||
Gemini::GeminiRequest request;
|
||||
request.set_url(url);
|
||||
|
||||
auto pipe_result = get_pipe_for_download();
|
||||
if (pipe_result.is_error())
|
||||
return {};
|
||||
|
||||
auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd);
|
||||
output_stream->make_unbuffered();
|
||||
auto job = Gemini::GeminiJob::construct(request, *output_stream);
|
||||
auto download = GeminiDownload::create_with_job({}, client, (Gemini::GeminiJob&)*job, move(output_stream));
|
||||
download->set_download_fd(pipe_result.value().read_fd);
|
||||
job->start();
|
||||
return download;
|
||||
}
|
||||
|
||||
}
|
41
Userland/Services/ProtocolServer/GeminiProtocol.h
Normal file
41
Userland/Services/ProtocolServer/GeminiProtocol.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ProtocolServer/Protocol.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class GeminiProtocol final : public Protocol {
|
||||
public:
|
||||
GeminiProtocol();
|
||||
virtual ~GeminiProtocol() override;
|
||||
|
||||
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>&, ReadonlyBytes body) override;
|
||||
};
|
||||
|
||||
}
|
74
Userland/Services/ProtocolServer/HttpDownload.cpp
Normal file
74
Userland/Services/ProtocolServer/HttpDownload.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 <LibHTTP/HttpJob.h>
|
||||
#include <LibHTTP/HttpResponse.h>
|
||||
#include <ProtocolServer/HttpDownload.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
HttpDownload::HttpDownload(ClientConnection& client, NonnullRefPtr<HTTP::HttpJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
: Download(client, move(output_stream))
|
||||
, m_job(job)
|
||||
{
|
||||
m_job->on_headers_received = [this](auto& headers, auto response_code) {
|
||||
if (response_code.has_value())
|
||||
set_status_code(response_code.value());
|
||||
set_response_headers(headers);
|
||||
};
|
||||
|
||||
m_job->on_finish = [this](bool success) {
|
||||
if (auto* response = m_job->response()) {
|
||||
set_status_code(response->code());
|
||||
set_response_headers(response->headers());
|
||||
set_downloaded_size(this->output_stream().size());
|
||||
}
|
||||
|
||||
// if we didn't know the total size, pretend that the download finished successfully
|
||||
// and set the total size to the downloaded size
|
||||
if (!total_size().has_value())
|
||||
did_progress(downloaded_size(), downloaded_size());
|
||||
|
||||
did_finish(success);
|
||||
};
|
||||
m_job->on_progress = [this](Optional<u32> total, u32 current) {
|
||||
did_progress(total, current);
|
||||
};
|
||||
}
|
||||
|
||||
HttpDownload::~HttpDownload()
|
||||
{
|
||||
m_job->on_finish = nullptr;
|
||||
m_job->on_progress = nullptr;
|
||||
m_job->shutdown();
|
||||
}
|
||||
|
||||
NonnullOwnPtr<HttpDownload> HttpDownload::create_with_job(Badge<HttpProtocol>, ClientConnection& client, NonnullRefPtr<HTTP::HttpJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
{
|
||||
return adopt_own(*new HttpDownload(client, move(job), move(output_stream)));
|
||||
}
|
||||
|
||||
}
|
47
Userland/Services/ProtocolServer/HttpDownload.h
Normal file
47
Userland/Services/ProtocolServer/HttpDownload.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibHTTP/Forward.h>
|
||||
#include <ProtocolServer/Download.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class HttpDownload final : public Download {
|
||||
public:
|
||||
virtual ~HttpDownload() override;
|
||||
static NonnullOwnPtr<HttpDownload> create_with_job(Badge<HttpProtocol>, ClientConnection&, NonnullRefPtr<HTTP::HttpJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
private:
|
||||
explicit HttpDownload(ClientConnection&, NonnullRefPtr<HTTP::HttpJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
NonnullRefPtr<HTTP::HttpJob> m_job;
|
||||
};
|
||||
|
||||
}
|
67
Userland/Services/ProtocolServer/HttpProtocol.cpp
Normal file
67
Userland/Services/ProtocolServer/HttpProtocol.cpp
Normal 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 <LibHTTP/HttpJob.h>
|
||||
#include <LibHTTP/HttpRequest.h>
|
||||
#include <ProtocolServer/HttpDownload.h>
|
||||
#include <ProtocolServer/HttpProtocol.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
HttpProtocol::HttpProtocol()
|
||||
: Protocol("http")
|
||||
{
|
||||
}
|
||||
|
||||
HttpProtocol::~HttpProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<Download> HttpProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, ReadonlyBytes body)
|
||||
{
|
||||
HTTP::HttpRequest request;
|
||||
if (method.equals_ignoring_case("post"))
|
||||
request.set_method(HTTP::HttpRequest::Method::POST);
|
||||
else
|
||||
request.set_method(HTTP::HttpRequest::Method::GET);
|
||||
request.set_url(url);
|
||||
request.set_headers(headers);
|
||||
request.set_body(body);
|
||||
|
||||
auto pipe_result = get_pipe_for_download();
|
||||
if (pipe_result.is_error())
|
||||
return {};
|
||||
|
||||
auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd);
|
||||
output_stream->make_unbuffered();
|
||||
auto job = HTTP::HttpJob::construct(request, *output_stream);
|
||||
auto download = HttpDownload::create_with_job({}, client, (HTTP::HttpJob&)*job, move(output_stream));
|
||||
download->set_download_fd(pipe_result.value().read_fd);
|
||||
job->start();
|
||||
return download;
|
||||
}
|
||||
|
||||
}
|
41
Userland/Services/ProtocolServer/HttpProtocol.h
Normal file
41
Userland/Services/ProtocolServer/HttpProtocol.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ProtocolServer/Protocol.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class HttpProtocol final : public Protocol {
|
||||
public:
|
||||
HttpProtocol();
|
||||
virtual ~HttpProtocol() override;
|
||||
|
||||
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) override;
|
||||
};
|
||||
|
||||
}
|
82
Userland/Services/ProtocolServer/HttpsDownload.cpp
Normal file
82
Userland/Services/ProtocolServer/HttpsDownload.cpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 <LibHTTP/HttpResponse.h>
|
||||
#include <LibHTTP/HttpsJob.h>
|
||||
#include <ProtocolServer/HttpsDownload.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
HttpsDownload::HttpsDownload(ClientConnection& client, NonnullRefPtr<HTTP::HttpsJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
: Download(client, move(output_stream))
|
||||
, m_job(job)
|
||||
{
|
||||
m_job->on_headers_received = [this](auto& headers, auto response_code) {
|
||||
if (response_code.has_value())
|
||||
set_status_code(response_code.value());
|
||||
set_response_headers(headers);
|
||||
};
|
||||
|
||||
m_job->on_finish = [this](bool success) {
|
||||
if (auto* response = m_job->response()) {
|
||||
set_status_code(response->code());
|
||||
set_response_headers(response->headers());
|
||||
set_downloaded_size(this->output_stream().size());
|
||||
}
|
||||
|
||||
// if we didn't know the total size, pretend that the download finished successfully
|
||||
// and set the total size to the downloaded size
|
||||
if (!total_size().has_value())
|
||||
did_progress(downloaded_size(), downloaded_size());
|
||||
|
||||
did_finish(success);
|
||||
};
|
||||
m_job->on_progress = [this](Optional<u32> total, u32 current) {
|
||||
did_progress(total, current);
|
||||
};
|
||||
m_job->on_certificate_requested = [this](auto&) {
|
||||
did_request_certificates();
|
||||
};
|
||||
}
|
||||
|
||||
void HttpsDownload::set_certificate(String certificate, String key)
|
||||
{
|
||||
m_job->set_certificate(move(certificate), move(key));
|
||||
}
|
||||
|
||||
HttpsDownload::~HttpsDownload()
|
||||
{
|
||||
m_job->on_finish = nullptr;
|
||||
m_job->on_progress = nullptr;
|
||||
m_job->shutdown();
|
||||
}
|
||||
|
||||
NonnullOwnPtr<HttpsDownload> HttpsDownload::create_with_job(Badge<HttpsProtocol>, ClientConnection& client, NonnullRefPtr<HTTP::HttpsJob> job, NonnullOwnPtr<OutputFileStream>&& output_stream)
|
||||
{
|
||||
return adopt_own(*new HttpsDownload(client, move(job), move(output_stream)));
|
||||
}
|
||||
|
||||
}
|
49
Userland/Services/ProtocolServer/HttpsDownload.h
Normal file
49
Userland/Services/ProtocolServer/HttpsDownload.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Badge.h>
|
||||
#include <LibCore/Forward.h>
|
||||
#include <LibHTTP/HttpsJob.h>
|
||||
#include <ProtocolServer/Download.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class HttpsDownload final : public Download {
|
||||
public:
|
||||
virtual ~HttpsDownload() override;
|
||||
static NonnullOwnPtr<HttpsDownload> create_with_job(Badge<HttpsProtocol>, ClientConnection&, NonnullRefPtr<HTTP::HttpsJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
private:
|
||||
explicit HttpsDownload(ClientConnection&, NonnullRefPtr<HTTP::HttpsJob>, NonnullOwnPtr<OutputFileStream>&&);
|
||||
|
||||
virtual void set_certificate(String certificate, String key) override;
|
||||
|
||||
NonnullRefPtr<HTTP::HttpsJob> m_job;
|
||||
};
|
||||
|
||||
}
|
67
Userland/Services/ProtocolServer/HttpsProtocol.cpp
Normal file
67
Userland/Services/ProtocolServer/HttpsProtocol.cpp
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2018-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 <LibHTTP/HttpRequest.h>
|
||||
#include <LibHTTP/HttpsJob.h>
|
||||
#include <ProtocolServer/HttpsDownload.h>
|
||||
#include <ProtocolServer/HttpsProtocol.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
HttpsProtocol::HttpsProtocol()
|
||||
: Protocol("https")
|
||||
{
|
||||
}
|
||||
|
||||
HttpsProtocol::~HttpsProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
OwnPtr<Download> HttpsProtocol::start_download(ClientConnection& client, const String& method, const URL& url, const HashMap<String, String>& headers, ReadonlyBytes body)
|
||||
{
|
||||
HTTP::HttpRequest request;
|
||||
if (method.equals_ignoring_case("post"))
|
||||
request.set_method(HTTP::HttpRequest::Method::POST);
|
||||
else
|
||||
request.set_method(HTTP::HttpRequest::Method::GET);
|
||||
request.set_url(url);
|
||||
request.set_headers(headers);
|
||||
request.set_body(body);
|
||||
|
||||
auto pipe_result = get_pipe_for_download();
|
||||
if (pipe_result.is_error())
|
||||
return {};
|
||||
|
||||
auto output_stream = make<OutputFileStream>(pipe_result.value().write_fd);
|
||||
output_stream->make_unbuffered();
|
||||
auto job = HTTP::HttpsJob::construct(request, *output_stream);
|
||||
auto download = HttpsDownload::create_with_job({}, client, (HTTP::HttpsJob&)*job, move(output_stream));
|
||||
download->set_download_fd(pipe_result.value().read_fd);
|
||||
job->start();
|
||||
return download;
|
||||
}
|
||||
|
||||
}
|
41
Userland/Services/ProtocolServer/HttpsProtocol.h
Normal file
41
Userland/Services/ProtocolServer/HttpsProtocol.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ProtocolServer/Protocol.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class HttpsProtocol final : public Protocol {
|
||||
public:
|
||||
HttpsProtocol();
|
||||
virtual ~HttpsProtocol() override;
|
||||
|
||||
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) override;
|
||||
};
|
||||
|
||||
}
|
67
Userland/Services/ProtocolServer/Protocol.cpp
Normal file
67
Userland/Services/ProtocolServer/Protocol.cpp
Normal 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 <AK/HashMap.h>
|
||||
#include <ProtocolServer/Protocol.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
static HashMap<String, Protocol*>& all_protocols()
|
||||
{
|
||||
static HashMap<String, Protocol*> map;
|
||||
return map;
|
||||
}
|
||||
|
||||
Protocol* Protocol::find_by_name(const String& name)
|
||||
{
|
||||
return all_protocols().get(name).value_or(nullptr);
|
||||
}
|
||||
|
||||
Protocol::Protocol(const String& name)
|
||||
{
|
||||
all_protocols().set(name, this);
|
||||
}
|
||||
|
||||
Protocol::~Protocol()
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
Result<Protocol::Pipe, String> Protocol::get_pipe_for_download()
|
||||
{
|
||||
int fd_pair[2] { 0 };
|
||||
if (pipe(fd_pair) != 0) {
|
||||
auto saved_errno = errno;
|
||||
dbgln("Protocol: pipe() failed: {}", strerror(saved_errno));
|
||||
return String { strerror(saved_errno) };
|
||||
}
|
||||
fcntl(fd_pair[1], F_SETFL, fcntl(fd_pair[1], F_GETFL) | O_NONBLOCK);
|
||||
return Pipe { fd_pair[0], fd_pair[1] };
|
||||
}
|
||||
|
||||
}
|
57
Userland/Services/ProtocolServer/Protocol.h
Normal file
57
Userland/Services/ProtocolServer/Protocol.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/Result.h>
|
||||
#include <AK/URL.h>
|
||||
#include <ProtocolServer/Forward.h>
|
||||
|
||||
namespace ProtocolServer {
|
||||
|
||||
class Protocol {
|
||||
public:
|
||||
virtual ~Protocol();
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
virtual OwnPtr<Download> start_download(ClientConnection&, const String& method, const URL&, const HashMap<String, String>& headers, ReadonlyBytes body) = 0;
|
||||
|
||||
static Protocol* find_by_name(const String&);
|
||||
|
||||
protected:
|
||||
explicit Protocol(const String& name);
|
||||
struct Pipe {
|
||||
int read_fd { -1 };
|
||||
int write_fd { -1 };
|
||||
};
|
||||
static Result<Pipe, String> get_pipe_for_download();
|
||||
|
||||
private:
|
||||
String m_name;
|
||||
};
|
||||
|
||||
}
|
10
Userland/Services/ProtocolServer/ProtocolClient.ipc
Normal file
10
Userland/Services/ProtocolServer/ProtocolClient.ipc
Normal file
|
@ -0,0 +1,10 @@
|
|||
endpoint ProtocolClient = 13
|
||||
{
|
||||
// Download notifications
|
||||
DownloadProgress(i32 download_id, Optional<u32> total_size, u32 downloaded_size) =|
|
||||
DownloadFinished(i32 download_id, bool success, u32 total_size) =|
|
||||
HeadersBecameAvailable(i32 download_id, IPC::Dictionary response_headers, Optional<u32> status_code) =|
|
||||
|
||||
// Certificate requests
|
||||
CertificateRequested(i32 download_id) => ()
|
||||
}
|
13
Userland/Services/ProtocolServer/ProtocolServer.ipc
Normal file
13
Userland/Services/ProtocolServer/ProtocolServer.ipc
Normal file
|
@ -0,0 +1,13 @@
|
|||
endpoint ProtocolServer = 9
|
||||
{
|
||||
// Basic protocol
|
||||
Greet() => (i32 client_id)
|
||||
|
||||
// Test if a specific protocol is supported, e.g "http"
|
||||
IsSupportedProtocol(String protocol) => (bool supported)
|
||||
|
||||
// Download API
|
||||
StartDownload(String method, URL url, IPC::Dictionary request_headers, ByteBuffer request_body) => (i32 download_id, Optional<IPC::File> response_fd)
|
||||
StopDownload(i32 download_id) => (bool success)
|
||||
SetCertificate(i32 download_id, String certificate, String key) => (bool success)
|
||||
}
|
69
Userland/Services/ProtocolServer/main.cpp
Normal file
69
Userland/Services/ProtocolServer/main.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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/EventLoop.h>
|
||||
#include <LibCore/LocalServer.h>
|
||||
#include <LibIPC/ClientConnection.h>
|
||||
#include <LibTLS/Certificate.h>
|
||||
#include <ProtocolServer/ClientConnection.h>
|
||||
#include <ProtocolServer/GeminiProtocol.h>
|
||||
#include <ProtocolServer/HttpProtocol.h>
|
||||
#include <ProtocolServer/HttpsProtocol.h>
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
if (pledge("stdio inet shared_buffer accept unix rpath cpath fattr sendfd recvfd", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Ensure the certificates are read out here.
|
||||
[[maybe_unused]] auto& certs = DefaultRootCACertificates::the();
|
||||
|
||||
Core::EventLoop event_loop;
|
||||
// FIXME: Establish a connection to LookupServer and then drop "unix"?
|
||||
if (pledge("stdio inet shared_buffer accept unix sendfd recvfd", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
if (unveil("/tmp/portal/lookup", "rw") < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
if (unveil(nullptr, nullptr) < 0) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
[[maybe_unused]] auto gemini = new ProtocolServer::GeminiProtocol;
|
||||
[[maybe_unused]] auto http = new ProtocolServer::HttpProtocol;
|
||||
[[maybe_unused]] auto https = new ProtocolServer::HttpsProtocol;
|
||||
|
||||
auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server();
|
||||
ASSERT(socket);
|
||||
IPC::new_client_connection<ProtocolServer::ClientConnection>(socket.release_nonnull(), 1);
|
||||
return event_loop.exec();
|
||||
}
|
7
Userland/Services/SystemMenu/CMakeLists.txt
Normal file
7
Userland/Services/SystemMenu/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
ShutdownDialog.cpp
|
||||
)
|
||||
|
||||
serenity_bin(SystemMenu)
|
||||
target_link_libraries(SystemMenu LibGUI LibDesktop)
|
117
Userland/Services/SystemMenu/ShutdownDialog.cpp
Normal file
117
Userland/Services/SystemMenu/ShutdownDialog.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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 "ShutdownDialog.h"
|
||||
#include <AK/String.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/BoxLayout.h>
|
||||
#include <LibGUI/Button.h>
|
||||
#include <LibGUI/Label.h>
|
||||
#include <LibGUI/RadioButton.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
#include <LibGfx/Font.h>
|
||||
#include <LibGfx/FontDatabase.h>
|
||||
|
||||
struct Option {
|
||||
String title;
|
||||
Vector<char const*> cmd;
|
||||
bool enabled;
|
||||
bool default_action;
|
||||
};
|
||||
|
||||
static const Vector<Option> options = {
|
||||
{ "Shut down", { "/bin/shutdown", "--now", nullptr }, true, true },
|
||||
{ "Restart", { "/bin/reboot", nullptr }, true, false },
|
||||
{ "Log out", {}, false, false },
|
||||
{ "Sleep", {}, false, false },
|
||||
};
|
||||
|
||||
Vector<char const*> ShutdownDialog::show()
|
||||
{
|
||||
auto dialog = ShutdownDialog::construct();
|
||||
auto rc = dialog->exec();
|
||||
if (rc == ExecResult::ExecOK && dialog->m_selected_option != -1)
|
||||
return options[dialog->m_selected_option].cmd;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ShutdownDialog::ShutdownDialog()
|
||||
: Dialog(nullptr)
|
||||
{
|
||||
resize(180, 180 + ((static_cast<int>(options.size()) - 3) * 16));
|
||||
center_on_screen();
|
||||
set_resizable(false);
|
||||
set_title("SerenityOS");
|
||||
set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png"));
|
||||
|
||||
auto& main = set_main_widget<GUI::Widget>();
|
||||
main.set_layout<GUI::VerticalBoxLayout>();
|
||||
main.layout()->set_margins({ 8, 8, 8, 8 });
|
||||
main.layout()->set_spacing(8);
|
||||
main.set_fill_with_background_color(true);
|
||||
|
||||
auto& header = main.add<GUI::Label>();
|
||||
header.set_text("What would you like to do?");
|
||||
header.set_fixed_height(16);
|
||||
header.set_font(Gfx::FontDatabase::default_bold_font());
|
||||
|
||||
for (size_t i = 0; i < options.size(); i++) {
|
||||
auto action = options[i];
|
||||
auto& radio = main.add<GUI::RadioButton>();
|
||||
radio.set_enabled(action.enabled);
|
||||
radio.set_text(action.title);
|
||||
|
||||
radio.on_checked = [this, i](auto) {
|
||||
m_selected_option = i;
|
||||
};
|
||||
|
||||
if (action.default_action) {
|
||||
radio.set_checked(true);
|
||||
m_selected_option = i;
|
||||
}
|
||||
}
|
||||
|
||||
auto& button_box = main.add<GUI::Widget>();
|
||||
button_box.set_layout<GUI::HorizontalBoxLayout>();
|
||||
button_box.layout()->set_spacing(8);
|
||||
|
||||
auto& ok_button = button_box.add<GUI::Button>();
|
||||
ok_button.on_click = [this](auto) {
|
||||
done(ExecResult::ExecOK);
|
||||
};
|
||||
ok_button.set_text("OK");
|
||||
|
||||
auto& cancel_button = button_box.add<GUI::Button>();
|
||||
cancel_button.on_click = [this](auto) {
|
||||
done(ExecResult::ExecCancel);
|
||||
};
|
||||
cancel_button.set_text("Cancel");
|
||||
}
|
||||
|
||||
ShutdownDialog::~ShutdownDialog()
|
||||
{
|
||||
}
|
43
Userland/Services/SystemMenu/ShutdownDialog.h
Normal file
43
Userland/Services/SystemMenu/ShutdownDialog.h
Normal 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/Dialog.h>
|
||||
|
||||
class ShutdownDialog : public GUI::Dialog {
|
||||
C_OBJECT(ShutdownDialog);
|
||||
|
||||
public:
|
||||
static Vector<char const*> show();
|
||||
|
||||
private:
|
||||
ShutdownDialog();
|
||||
virtual ~ShutdownDialog() override;
|
||||
|
||||
int m_selected_option { -1 };
|
||||
};
|
232
Userland/Services/SystemMenu/main.cpp
Normal file
232
Userland/Services/SystemMenu/main.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* 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 "ShutdownDialog.h"
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibDesktop/AppFile.h>
|
||||
#include <LibGUI/Action.h>
|
||||
#include <LibGUI/ActionGroup.h>
|
||||
#include <LibGUI/Application.h>
|
||||
#include <LibGUI/FileIconProvider.h>
|
||||
#include <LibGUI/Icon.h>
|
||||
#include <LibGUI/Menu.h>
|
||||
#include <LibGUI/WindowServerConnection.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <serenity.h>
|
||||
#include <spawn.h>
|
||||
|
||||
//#define SYSTEM_MENU_DEBUG
|
||||
|
||||
struct AppMetadata {
|
||||
String executable;
|
||||
String name;
|
||||
String category;
|
||||
};
|
||||
Vector<AppMetadata> g_apps;
|
||||
|
||||
struct ThemeMetadata {
|
||||
String name;
|
||||
String path;
|
||||
};
|
||||
|
||||
Color g_menu_selection_color;
|
||||
|
||||
Vector<ThemeMetadata> g_themes;
|
||||
RefPtr<GUI::Menu> g_themes_menu;
|
||||
GUI::ActionGroup g_themes_group;
|
||||
|
||||
static Vector<String> discover_apps_and_categories();
|
||||
static NonnullRefPtr<GUI::Menu> build_system_menu();
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
auto app = GUI::Application::construct(argc, argv);
|
||||
app->set_quit_when_last_window_deleted(false);
|
||||
|
||||
auto menu = build_system_menu();
|
||||
menu->realize_menu_if_needed();
|
||||
|
||||
GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemMenu>(menu->menu_id());
|
||||
|
||||
if (pledge("stdio shared_buffer accept rpath proc exec", nullptr) < 0) {
|
||||
perror("pledge");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (chdir(Core::StandardPaths::home_directory().characters()) < 0) {
|
||||
perror("chdir");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/bin", "x")) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (unveil("/res", "r")) {
|
||||
perror("unveil");
|
||||
return 1;
|
||||
}
|
||||
|
||||
unveil(nullptr, nullptr);
|
||||
|
||||
return app->exec();
|
||||
}
|
||||
|
||||
Vector<String> discover_apps_and_categories()
|
||||
{
|
||||
HashTable<String> seen_app_categories;
|
||||
Desktop::AppFile::for_each([&](auto af) {
|
||||
g_apps.append({ af->executable(), af->name(), af->category() });
|
||||
seen_app_categories.set(af->category());
|
||||
});
|
||||
quick_sort(g_apps, [](auto& a, auto& b) { return a.name < b.name; });
|
||||
|
||||
Vector<String> sorted_app_categories;
|
||||
for (auto& category : seen_app_categories) {
|
||||
sorted_app_categories.append(category);
|
||||
}
|
||||
quick_sort(sorted_app_categories);
|
||||
|
||||
return sorted_app_categories;
|
||||
}
|
||||
|
||||
NonnullRefPtr<GUI::Menu> build_system_menu()
|
||||
{
|
||||
const Vector<String> sorted_app_categories = discover_apps_and_categories();
|
||||
auto system_menu = GUI::Menu::construct("\xE2\x9A\xA1"); // HIGH VOLTAGE SIGN
|
||||
|
||||
// First we construct all the necessary app category submenus.
|
||||
HashMap<String, NonnullRefPtr<GUI::Menu>> app_category_menus;
|
||||
auto category_icons = Core::ConfigFile::open("/res/icons/SystemMenu.ini");
|
||||
for (const auto& category : sorted_app_categories) {
|
||||
if (app_category_menus.contains(category))
|
||||
continue;
|
||||
auto& category_menu = system_menu->add_submenu(category);
|
||||
auto category_icon_path = category_icons->read_entry("16x16", category);
|
||||
if (!category_icon_path.is_empty()) {
|
||||
auto icon = Gfx::Bitmap::load_from_file(category_icon_path);
|
||||
category_menu.set_icon(icon);
|
||||
}
|
||||
app_category_menus.set(category, category_menu);
|
||||
}
|
||||
|
||||
// Then we create and insert all the app menu items into the right place.
|
||||
int app_identifier = 0;
|
||||
for (const auto& app : g_apps) {
|
||||
auto icon = GUI::FileIconProvider::icon_for_executable(app.executable).bitmap_for_size(16);
|
||||
|
||||
#ifdef SYSTEM_MENU_DEBUG
|
||||
if (icon)
|
||||
dbg() << "App " << app.name << " has icon with size " << icon->size();
|
||||
#endif
|
||||
|
||||
auto parent_menu = app_category_menus.get(app.category).value_or(*system_menu);
|
||||
parent_menu->add_action(GUI::Action::create(app.name, icon, [app_identifier](auto&) {
|
||||
dbg() << "Activated app with ID " << app_identifier;
|
||||
const auto& bin = g_apps[app_identifier].executable;
|
||||
pid_t child_pid;
|
||||
const char* argv[] = { bin.characters(), nullptr };
|
||||
if ((errno = posix_spawn(&child_pid, bin.characters(), nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
||||
perror("posix_spawn");
|
||||
} else {
|
||||
if (disown(child_pid) < 0)
|
||||
perror("disown");
|
||||
}
|
||||
}));
|
||||
++app_identifier;
|
||||
}
|
||||
|
||||
system_menu->add_separator();
|
||||
|
||||
g_themes_group.set_exclusive(true);
|
||||
g_themes_group.set_unchecking_allowed(false);
|
||||
|
||||
g_themes_menu = &system_menu->add_submenu("Themes");
|
||||
g_themes_menu->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/themes.png"));
|
||||
|
||||
{
|
||||
Core::DirIterator dt("/res/themes", Core::DirIterator::SkipDots);
|
||||
while (dt.has_next()) {
|
||||
auto theme_name = dt.next_path();
|
||||
auto theme_path = String::format("/res/themes/%s", theme_name.characters());
|
||||
g_themes.append({ LexicalPath(theme_name).title(), theme_path });
|
||||
}
|
||||
quick_sort(g_themes, [](auto& a, auto& b) { return a.name < b.name; });
|
||||
}
|
||||
|
||||
auto current_theme_name = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::GetSystemTheme>()->theme_name();
|
||||
|
||||
{
|
||||
int theme_identifier = 0;
|
||||
for (auto& theme : g_themes) {
|
||||
auto action = GUI::Action::create_checkable(theme.name, [theme_identifier](auto&) {
|
||||
auto& theme = g_themes[theme_identifier];
|
||||
dbg() << "Theme switched to " << theme.name << " at path " << theme.path;
|
||||
auto response = GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::SetSystemTheme>(theme.path, theme.name);
|
||||
ASSERT(response->success());
|
||||
});
|
||||
if (theme.name == current_theme_name)
|
||||
action->set_checked(true);
|
||||
g_themes_group.add_action(action);
|
||||
g_themes_menu->add_action(action);
|
||||
++theme_identifier;
|
||||
}
|
||||
}
|
||||
|
||||
system_menu->add_separator();
|
||||
system_menu->add_action(GUI::Action::create("About SerenityOS", Gfx::Bitmap::load_from_file("/res/icons/16x16/ladybug.png"), [](auto&) {
|
||||
pid_t child_pid;
|
||||
const char* argv[] = { "/bin/About", nullptr };
|
||||
if ((errno = posix_spawn(&child_pid, "/bin/About", nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
||||
perror("posix_spawn");
|
||||
} else {
|
||||
if (disown(child_pid) < 0)
|
||||
perror("disown");
|
||||
}
|
||||
}));
|
||||
system_menu->add_separator();
|
||||
system_menu->add_action(GUI::Action::create("Exit...", Gfx::Bitmap::load_from_file("/res/icons/16x16/power.png"), [](auto&) {
|
||||
auto command = ShutdownDialog::show();
|
||||
|
||||
if (command.size() == 0)
|
||||
return;
|
||||
|
||||
pid_t child_pid;
|
||||
if ((errno = posix_spawn(&child_pid, command[0], nullptr, nullptr, const_cast<char**>(command.data()), environ))) {
|
||||
perror("posix_spawn");
|
||||
} else {
|
||||
if (disown(child_pid) < 0)
|
||||
perror("disown");
|
||||
}
|
||||
}));
|
||||
|
||||
return system_menu;
|
||||
}
|
7
Userland/Services/SystemServer/CMakeLists.txt
Normal file
7
Userland/Services/SystemServer/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(SOURCES
|
||||
main.cpp
|
||||
Service.cpp
|
||||
)
|
||||
|
||||
serenity_bin(SystemServer)
|
||||
target_link_libraries(SystemServer LibCore)
|
376
Userland/Services/SystemServer/Service.cpp
Normal file
376
Userland/Services/SystemServer/Service.cpp
Normal file
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* 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 "Service.h"
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <LibCore/ConfigFile.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/Socket.h>
|
||||
#include <grp.h>
|
||||
#include <libgen.h>
|
||||
#include <pwd.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static HashMap<pid_t, Service*> s_service_map;
|
||||
|
||||
Service* Service::find_by_pid(pid_t pid)
|
||||
{
|
||||
auto it = s_service_map.find(pid);
|
||||
if (it == s_service_map.end())
|
||||
return nullptr;
|
||||
return (*it).value;
|
||||
}
|
||||
|
||||
void Service::setup_socket()
|
||||
{
|
||||
ASSERT(!m_socket_path.is_null());
|
||||
ASSERT(m_socket_fd == -1);
|
||||
|
||||
auto ok = Core::File::ensure_parent_directories(m_socket_path);
|
||||
ASSERT(ok);
|
||||
|
||||
// Note: we use SOCK_CLOEXEC here to make sure we don't leak every socket to
|
||||
// all the clients. We'll make the one we do need to pass down !CLOEXEC later
|
||||
// after forking off the process.
|
||||
m_socket_fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
|
||||
if (m_socket_fd < 0) {
|
||||
perror("socket");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (m_account.has_value()) {
|
||||
auto& account = m_account.value();
|
||||
if (fchown(m_socket_fd, account.uid(), account.gid()) < 0) {
|
||||
perror("fchown");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
if (fchmod(m_socket_fd, m_socket_permissions) < 0) {
|
||||
perror("fchmod");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
auto socket_address = Core::SocketAddress::local(m_socket_path);
|
||||
auto un_optional = socket_address.to_sockaddr_un();
|
||||
if (!un_optional.has_value()) {
|
||||
dbg() << "Socket name " << m_socket_path << " is too long. BUG! This should have failed earlier!";
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
auto un = un_optional.value();
|
||||
int rc = bind(m_socket_fd, (const sockaddr*)&un, sizeof(un));
|
||||
if (rc < 0) {
|
||||
perror("bind");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
rc = listen(m_socket_fd, 16);
|
||||
if (rc < 0) {
|
||||
perror("listen");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
void Service::setup_notifier()
|
||||
{
|
||||
ASSERT(m_lazy);
|
||||
ASSERT(m_socket_fd >= 0);
|
||||
ASSERT(!m_socket_notifier);
|
||||
|
||||
m_socket_notifier = Core::Notifier::construct(m_socket_fd, Core::Notifier::Event::Read, this);
|
||||
m_socket_notifier->on_ready_to_read = [this] {
|
||||
handle_socket_connection();
|
||||
};
|
||||
}
|
||||
|
||||
void Service::handle_socket_connection()
|
||||
{
|
||||
#ifdef SERVICE_DEBUG
|
||||
dbg() << "Ready to read on behalf of " << name();
|
||||
#endif
|
||||
if (m_accept_socket_connections) {
|
||||
int accepted_fd = accept(m_socket_fd, nullptr, nullptr);
|
||||
if (accepted_fd < 0) {
|
||||
perror("accept");
|
||||
return;
|
||||
}
|
||||
spawn(accepted_fd);
|
||||
close(accepted_fd);
|
||||
} else {
|
||||
remove_child(*m_socket_notifier);
|
||||
m_socket_notifier = nullptr;
|
||||
spawn(m_socket_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void Service::activate()
|
||||
{
|
||||
ASSERT(m_pid < 0);
|
||||
|
||||
if (m_lazy)
|
||||
setup_notifier();
|
||||
else
|
||||
spawn(m_socket_fd);
|
||||
}
|
||||
|
||||
void Service::spawn(int socket_fd)
|
||||
{
|
||||
#ifdef SERVICE_DEBUG
|
||||
dbg() << "Spawning " << name();
|
||||
#endif
|
||||
|
||||
m_run_timer.start();
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
dbg() << "Failed to spawn " << name() << ". Sucks, dude :(";
|
||||
} else if (pid == 0) {
|
||||
// We are the child.
|
||||
|
||||
if (!m_working_directory.is_null()) {
|
||||
if (chdir(m_working_directory.characters()) < 0) {
|
||||
perror("chdir");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
struct sched_param p;
|
||||
p.sched_priority = m_priority;
|
||||
int rc = sched_setparam(0, &p);
|
||||
if (rc < 0) {
|
||||
perror("sched_setparam");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (!m_stdio_file_path.is_null()) {
|
||||
close(STDIN_FILENO);
|
||||
int fd = open_with_path_length(m_stdio_file_path.characters(), m_stdio_file_path.length(), O_RDWR, 0);
|
||||
ASSERT(fd <= 0);
|
||||
if (fd < 0) {
|
||||
perror("open");
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY);
|
||||
}
|
||||
} else {
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
ioctl(STDIN_FILENO, TIOCNOTTY);
|
||||
}
|
||||
close(STDIN_FILENO);
|
||||
close(STDOUT_FILENO);
|
||||
close(STDERR_FILENO);
|
||||
|
||||
int fd = open("/dev/null", O_RDWR);
|
||||
ASSERT(fd == STDIN_FILENO);
|
||||
dup2(STDIN_FILENO, STDOUT_FILENO);
|
||||
dup2(STDIN_FILENO, STDERR_FILENO);
|
||||
}
|
||||
|
||||
if (socket_fd >= 0) {
|
||||
ASSERT(!m_socket_path.is_null());
|
||||
ASSERT(socket_fd > 3);
|
||||
dup2(socket_fd, 3);
|
||||
// The new descriptor is !CLOEXEC here.
|
||||
setenv("SOCKET_TAKEOVER", "1", true);
|
||||
}
|
||||
|
||||
if (m_account.has_value()) {
|
||||
auto& account = m_account.value();
|
||||
if (setgid(account.gid()) < 0 || setgroups(account.extra_gids().size(), account.extra_gids().data()) < 0 || setuid(account.uid()) < 0) {
|
||||
dbgln("Failed to drop privileges (GID={}, UID={})\n", account.gid(), account.uid());
|
||||
exit(1);
|
||||
}
|
||||
setenv("HOME", account.home_directory().characters(), true);
|
||||
}
|
||||
|
||||
for (String& env : m_environment)
|
||||
putenv(const_cast<char*>(env.characters()));
|
||||
|
||||
char* argv[m_extra_arguments.size() + 2];
|
||||
argv[0] = const_cast<char*>(m_executable_path.characters());
|
||||
for (size_t i = 0; i < m_extra_arguments.size(); i++)
|
||||
argv[i + 1] = const_cast<char*>(m_extra_arguments[i].characters());
|
||||
argv[m_extra_arguments.size() + 1] = nullptr;
|
||||
|
||||
rc = execv(argv[0], argv);
|
||||
perror("exec");
|
||||
ASSERT_NOT_REACHED();
|
||||
} else if (!m_multi_instance) {
|
||||
// We are the parent.
|
||||
m_pid = pid;
|
||||
s_service_map.set(pid, this);
|
||||
}
|
||||
}
|
||||
|
||||
void Service::did_exit(int exit_code)
|
||||
{
|
||||
ASSERT(m_pid > 0);
|
||||
ASSERT(!m_multi_instance);
|
||||
|
||||
dbg() << "Service " << name() << " has exited with exit code " << exit_code;
|
||||
|
||||
s_service_map.remove(m_pid);
|
||||
m_pid = -1;
|
||||
|
||||
if (!m_keep_alive)
|
||||
return;
|
||||
|
||||
int run_time_in_msec = m_run_timer.elapsed();
|
||||
bool exited_successfully = exit_code == 0;
|
||||
|
||||
if (!exited_successfully && run_time_in_msec < 1000) {
|
||||
switch (m_restart_attempts) {
|
||||
case 0:
|
||||
dbgln("Trying again");
|
||||
break;
|
||||
case 1:
|
||||
dbgln("Third time's a charm?");
|
||||
break;
|
||||
default:
|
||||
dbg() << "Giving up on " << name() << ". Good luck!";
|
||||
return;
|
||||
}
|
||||
m_restart_attempts++;
|
||||
}
|
||||
|
||||
activate();
|
||||
}
|
||||
|
||||
Service::Service(const Core::ConfigFile& config, const StringView& name)
|
||||
: Core::Object(nullptr)
|
||||
{
|
||||
ASSERT(config.has_group(name));
|
||||
|
||||
set_name(name);
|
||||
m_executable_path = config.read_entry(name, "Executable", String::format("/bin/%s", this->name().characters()));
|
||||
m_extra_arguments = config.read_entry(name, "Arguments", "").split(' ');
|
||||
m_stdio_file_path = config.read_entry(name, "StdIO");
|
||||
|
||||
String prio = config.read_entry(name, "Priority");
|
||||
if (prio == "low")
|
||||
m_priority = 10;
|
||||
else if (prio == "normal" || prio.is_null())
|
||||
m_priority = 30;
|
||||
else if (prio == "high")
|
||||
m_priority = 50;
|
||||
else
|
||||
ASSERT_NOT_REACHED();
|
||||
|
||||
m_keep_alive = config.read_bool_entry(name, "KeepAlive");
|
||||
m_lazy = config.read_bool_entry(name, "Lazy");
|
||||
|
||||
m_user = config.read_entry(name, "User");
|
||||
if (!m_user.is_null()) {
|
||||
auto result = Core::Account::from_name(m_user.characters());
|
||||
if (result.is_error())
|
||||
warnln("Failed to resolve user {}: {}", m_user, result.error());
|
||||
else
|
||||
m_account = result.value();
|
||||
}
|
||||
|
||||
m_working_directory = config.read_entry(name, "WorkingDirectory");
|
||||
m_environment = config.read_entry(name, "Environment").split(' ');
|
||||
m_boot_modes = config.read_entry(name, "BootModes", "graphical").split(',');
|
||||
m_multi_instance = config.read_bool_entry(name, "MultiInstance");
|
||||
m_accept_socket_connections = config.read_bool_entry(name, "AcceptSocketConnections");
|
||||
|
||||
m_socket_path = config.read_entry(name, "Socket");
|
||||
|
||||
// Lazy requires Socket.
|
||||
ASSERT(!m_lazy || !m_socket_path.is_null());
|
||||
// AcceptSocketConnections always requires Socket, Lazy, and MultiInstance.
|
||||
ASSERT(!m_accept_socket_connections || (!m_socket_path.is_null() && m_lazy && m_multi_instance));
|
||||
// MultiInstance doesn't work with KeepAlive.
|
||||
ASSERT(!m_multi_instance || !m_keep_alive);
|
||||
// Socket path (plus NUL) must fit into the structs sent to the Kernel.
|
||||
ASSERT(m_socket_path.length() < UNIX_PATH_MAX);
|
||||
|
||||
if (!m_socket_path.is_null() && is_enabled()) {
|
||||
auto socket_permissions_string = config.read_entry(name, "SocketPermissions", "0600");
|
||||
m_socket_permissions = strtol(socket_permissions_string.characters(), nullptr, 8) & 04777;
|
||||
setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
void Service::save_to(JsonObject& json)
|
||||
{
|
||||
Core::Object::save_to(json);
|
||||
|
||||
json.set("executable_path", m_executable_path);
|
||||
|
||||
// FIXME: This crashes Inspector.
|
||||
/*
|
||||
JsonArray extra_args;
|
||||
for (String& arg : m_extra_arguments)
|
||||
extra_args.append(arg);
|
||||
json.set("extra_arguments", move(extra_args));
|
||||
|
||||
JsonArray boot_modes;
|
||||
for (String& mode : m_boot_modes)
|
||||
boot_modes.append(mode);
|
||||
json.set("boot_modes", boot_modes);
|
||||
|
||||
JsonArray environment;
|
||||
for (String& env : m_environment)
|
||||
boot_modes.append(env);
|
||||
json.set("environment", environment);
|
||||
*/
|
||||
|
||||
json.set("stdio_file_path", m_stdio_file_path);
|
||||
json.set("priority", m_priority);
|
||||
json.set("keep_alive", m_keep_alive);
|
||||
json.set("socket_path", m_socket_path);
|
||||
json.set("socket_permissions", m_socket_permissions);
|
||||
json.set("lazy", m_lazy);
|
||||
json.set("user", m_user);
|
||||
json.set("multi_instance", m_multi_instance);
|
||||
json.set("accept_socket_connections", m_accept_socket_connections);
|
||||
|
||||
if (m_pid > 0)
|
||||
json.set("pid", m_pid);
|
||||
else
|
||||
json.set("pid", nullptr);
|
||||
|
||||
json.set("restart_attempts", m_restart_attempts);
|
||||
json.set("working_directory", m_working_directory);
|
||||
}
|
||||
|
||||
bool Service::is_enabled() const
|
||||
{
|
||||
extern String g_boot_mode;
|
||||
return m_boot_modes.contains_slow(g_boot_mode);
|
||||
}
|
102
Userland/Services/SystemServer/Service.h
Normal file
102
Userland/Services/SystemServer/Service.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefPtr.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibCore/Account.h>
|
||||
#include <LibCore/ElapsedTimer.h>
|
||||
#include <LibCore/Notifier.h>
|
||||
#include <LibCore/Object.h>
|
||||
|
||||
class Service final : public Core::Object {
|
||||
C_OBJECT(Service)
|
||||
|
||||
public:
|
||||
bool is_enabled() const;
|
||||
void activate();
|
||||
void did_exit(int exit_code);
|
||||
|
||||
static Service* find_by_pid(pid_t);
|
||||
|
||||
// FIXME: Port to Core::Property
|
||||
void save_to(AK::JsonObject&);
|
||||
|
||||
private:
|
||||
Service(const Core::ConfigFile&, const StringView& name);
|
||||
|
||||
void spawn(int socket_fd = -1);
|
||||
|
||||
// Path to the executable. By default this is /bin/{m_name}.
|
||||
String m_executable_path;
|
||||
// Extra arguments, starting from argv[1], to pass when exec'ing.
|
||||
Vector<String> m_extra_arguments;
|
||||
// File path to open as stdio fds.
|
||||
String m_stdio_file_path;
|
||||
int m_priority { 1 };
|
||||
// Whether we should re-launch it if it exits.
|
||||
bool m_keep_alive { false };
|
||||
// Path to the socket to create and listen on on behalf of this service.
|
||||
String m_socket_path;
|
||||
// File system permissions for the socket.
|
||||
mode_t m_socket_permissions { 0 };
|
||||
// Whether we should accept connections on the socket and pass the accepted
|
||||
// (and not listening) socket to the service. This requires a multi-instance
|
||||
// service.
|
||||
bool m_accept_socket_connections { false };
|
||||
// Whether we should only spawn this service once somebody connects to the socket.
|
||||
bool m_lazy;
|
||||
// The name of the user we should run this service as.
|
||||
String m_user;
|
||||
// The working directory in which to spawn the service.
|
||||
String m_working_directory;
|
||||
// Boot modes to run this service in. By default, this is the graphical mode.
|
||||
Vector<String> m_boot_modes;
|
||||
// Whether several instances of this service can run at once.
|
||||
bool m_multi_instance { false };
|
||||
// Environment variables to pass to the service.
|
||||
Vector<String> m_environment;
|
||||
|
||||
// The resolved user account to run this service as.
|
||||
Optional<Core::Account> m_account;
|
||||
|
||||
// For single-instance services, PID of the running instance of this service.
|
||||
pid_t m_pid { -1 };
|
||||
// An open fd to the socket.
|
||||
int m_socket_fd { -1 };
|
||||
RefPtr<Core::Notifier> m_socket_notifier;
|
||||
|
||||
// Timer since we last spawned the service.
|
||||
Core::ElapsedTimer m_run_timer;
|
||||
// How many times we have tried to restart this service, only counting those
|
||||
// times where it has exited unsuccessfully and too quickly.
|
||||
int m_restart_attempts { 0 };
|
||||
|
||||
void setup_socket();
|
||||
void setup_notifier();
|
||||
void handle_socket_connection();
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue