1 : /*
2 : * Copyright (c) 2012 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can be
4 : * found in the LICENSE file.
5 : */
6 :
7 : #include <cstring>
8 : #include <string>
9 : #include <vector>
10 :
11 : #include "native_client/src/include/nacl_scoped_ptr.h"
12 : #include "native_client/src/shared/platform/nacl_log.h"
13 : #include "native_client/src/shared/platform/nacl_time.h"
14 : #include "native_client/src/shared/srpc/nacl_srpc.h"
15 :
16 : #include "native_client/src/trusted/sel_universal/parsing.h"
17 : #include "native_client/src/trusted/sel_universal/pnacl_emu_handler.h"
18 : #include "native_client/src/trusted/sel_universal/rpc_universal.h"
19 :
20 : using std::string;
21 : using std::vector;
22 :
23 : namespace {
24 :
25 : int
26 0 : SendDataChunk(NaClCommandLoop* ncl, nacl_abi_size_t size, const char *data) {
27 0 : const string signature = string("StreamChunk:C:");
28 0 : NaClSrpcArg in[1];
29 0 : NaClSrpcArg* inv[2];
30 0 : BuildArgVec(inv, in, 1);
31 0 : in[0].tag = NACL_SRPC_ARG_TYPE_CHAR_ARRAY;
32 0 : in[0].arrays.carr = static_cast<char*>(malloc(size));
33 0 : if (0 == in[0].arrays.carr) {
34 0 : NaClLog(LOG_ERROR, "allocation failed\n");
35 0 : FreeArrayArgs(inv);
36 0 : return -1;
37 : }
38 0 : in[0].u.count = size;
39 0 : memcpy(in[0].arrays.carr, data, size);
40 :
41 0 : NaClSrpcArg* outv[1];
42 0 : outv[0] = NULL;
43 0 : if (!ncl->InvokeNexeRpc(signature, inv, outv)) {
44 0 : NaClLog(LOG_ERROR, "StreamChunk failed\n");
45 0 : FreeArrayArgs(inv);
46 0 : return -1;
47 : }
48 0 : return 0;
49 0 : }
50 :
51 : // Stream the file to the client with a series of StreamChunk RPCs.
52 : // Rate-limit the sending to bits_per_sec to emulate a download
53 0 : bool PnaclStreamFile(NaClCommandLoop* ncl, FILE* input_file,
54 0 : int chunk_size, int bits_per_sec) {
55 0 : nacl::scoped_array<char> data(new char[chunk_size]);
56 0 : NaClLog(LOG_INFO, "Streaming file at %d bps\n", bits_per_sec);
57 0 : uint64_t start_time = NaClGetTimeOfDayMicroseconds();
58 0 : size_t data_sent = 0;
59 0 : while (!feof(input_file) && !ferror(input_file)) {
60 0 : size_t data_read = fread(data.get(), 1, chunk_size, input_file);
61 0 : uint64_t cur_elapsed_us = NaClGetTimeOfDayMicroseconds() - start_time;
62 0 : uint64_t target_elapsed_us = static_cast<uint64_t>(data_sent + data_read)
63 : * 8 * 1000000 / bits_per_sec;
64 0 : if (cur_elapsed_us < target_elapsed_us) {
65 0 : uint64_t sleep_us = target_elapsed_us - cur_elapsed_us;
66 0 : struct nacl_abi_timespec req;
67 0 : struct nacl_abi_timespec rem;
68 0 : req.tv_sec = sleep_us / 1000000;
69 0 : req.tv_nsec = sleep_us % 1000000 * 1000;
70 0 : int ret = NaClNanosleep(&req, &rem);
71 0 : if (ret == -1) {
72 0 : NaClLog(LOG_ERROR, "NaClNanosleep failed");
73 0 : return false;
74 : }
75 0 : }
76 0 : if (SendDataChunk(ncl, static_cast<nacl_abi_size_t>(data_read),
77 0 : data.get())) {
78 : // If SendDataChunkFails, just return immediately, but don't fail.
79 : // This will cause the final RPC to be run by the script to get the
80 : // error string.
81 0 : NaClLog(LOG_ERROR, "stream_file: SendDataChunk failed, but returning"
82 : " without failing. Expect call to StreamEnd.");
83 0 : return true;
84 : }
85 0 : data_sent += data_read;
86 0 : }
87 0 : return true;
88 0 : }
89 :
90 : } // namespace
91 :
92 0 : bool HandlerPnaclFileStream(NaClCommandLoop* ncl,
93 0 : const vector<string>& args) {
94 0 : if (args.size() != 4) {
95 0 : NaClLog(LOG_ERROR, "not enough args to file_stream\n");
96 0 : return false;
97 : }
98 :
99 0 : FILE* input_file = fopen(args[1].c_str(), "rb");
100 0 : if (NULL == input_file) {
101 0 : NaClLog(LOG_ERROR, "could not open input file %s\n", args[1].c_str());
102 0 : return false;
103 : }
104 0 : return PnaclStreamFile(ncl, input_file, strtol(args[2].c_str(), 0, 0),
105 0 : strtol(args[3].c_str(), 0, 0));
106 0 : }
|