1// Copyright 2016 The Fuchsia Authors
2//
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT
6
7#include <err.h>
8#include <inttypes.h>
9#include <trace.h>
10
11#include <lib/ktrace.h>
12#include <object/handle.h>
13#include <object/port_dispatcher.h>
14#include <object/process_dispatcher.h>
15
16#include <fbl/alloc_checker.h>
17#include <fbl/ref_ptr.h>
18
19#include <zircon/syscalls/policy.h>
20#include <zircon/types.h>
21
22#include "priv.h"
23
24#define LOCAL_TRACE 0
25
26zx_status_t sys_port_create(uint32_t options, user_out_handle* out) {
27    LTRACEF("options %u\n", options);
28    auto up = ProcessDispatcher::GetCurrent();
29    zx_status_t result = up->QueryPolicy(ZX_POL_NEW_PORT);
30    if (result != ZX_OK)
31        return result;
32
33    fbl::RefPtr<Dispatcher> dispatcher;
34    zx_rights_t rights;
35
36    result = PortDispatcher::Create(options, &dispatcher, &rights);
37    if (result != ZX_OK)
38        return result;
39
40    uint32_t koid = (uint32_t)dispatcher->get_koid();
41
42    result = out->make(fbl::move(dispatcher), rights);
43
44    ktrace(TAG_PORT_CREATE, koid, 0, 0, 0);
45    return result;
46}
47
48zx_status_t sys_port_queue(zx_handle_t handle, user_in_ptr<const zx_port_packet_t> packet_in) {
49    LTRACEF("handle %x\n", handle);
50
51    auto up = ProcessDispatcher::GetCurrent();
52
53    fbl::RefPtr<PortDispatcher> port;
54    zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &port);
55    if (status != ZX_OK)
56        return status;
57
58    zx_port_packet_t packet;
59    status = packet_in.copy_from_user(&packet);
60    if (status != ZX_OK)
61        return status;
62
63    return port->QueueUser(packet);
64}
65
66zx_status_t sys_port_wait(zx_handle_t handle, zx_time_t deadline,
67                          user_out_ptr<zx_port_packet_t> packet_out) {
68    LTRACEF("handle %x\n", handle);
69
70    auto up = ProcessDispatcher::GetCurrent();
71
72    fbl::RefPtr<PortDispatcher> port;
73    zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ, &port);
74    if (status != ZX_OK)
75        return status;
76
77    ktrace(TAG_PORT_WAIT, (uint32_t)port->get_koid(), 0, 0, 0);
78
79    zx_port_packet_t pp;
80    zx_status_t st = port->Dequeue(deadline, &pp);
81
82    ktrace(TAG_PORT_WAIT_DONE, (uint32_t)port->get_koid(), st, 0, 0);
83
84    if (st != ZX_OK)
85        return st;
86
87    status = packet_out.copy_to_user(pp);
88    if (status != ZX_OK)
89        return status;
90
91    return ZX_OK;
92}
93
94zx_status_t sys_port_cancel(zx_handle_t handle, zx_handle_t source, uint64_t key) {
95    auto up = ProcessDispatcher::GetCurrent();
96
97    fbl::RefPtr<PortDispatcher> port;
98    zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &port);
99    if (status != ZX_OK)
100        return status;
101
102    {
103        Guard<fbl::Mutex> guard{up->handle_table_lock()};
104        Handle* watched = up->GetHandleLocked(source);
105        if (!watched)
106            return ZX_ERR_BAD_HANDLE;
107        if (!watched->HasRights(ZX_RIGHT_WAIT))
108            return ZX_ERR_ACCESS_DENIED;
109
110        auto dispatcher = watched->dispatcher();
111        if (!dispatcher->has_state_tracker())
112            return ZX_ERR_NOT_SUPPORTED;
113
114        bool had_observer = dispatcher->CancelByKey(watched, port.get(), key);
115        bool packet_removed = port->CancelQueued(watched, key);
116        return (had_observer || packet_removed) ? ZX_OK : ZX_ERR_NOT_FOUND;
117    }
118}
119