/*
* Unix SMB/CIFS implementation.
*
* Helpers around tevent_req_profile
*
* Copyright (C) Volker Lendecke 2018
*
* ** NOTE! The following LGPL license applies to the tevent
* ** library. This does NOT imply that all of Samba is released
* ** under the LGPL
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see .
*/
#include "replace.h"
#include
#include "lib/util/tevent_req_profile.h"
#include "lib/util/time_basic.h"
#include "lib/util/memory.h"
static bool tevent_req_profile_string_internal(
const struct tevent_req_profile *profile,
unsigned indent,
unsigned max_indent,
char **string)
{
struct timeval start, stop, diff;
struct timeval_buf start_buf, stop_buf;
const char *req_name = NULL;
const char *start_location = NULL;
const char *stop_location = NULL;
pid_t pid;
enum tevent_req_state state;
const char *state_buf = NULL;
uint64_t user_error;
const struct tevent_req_profile *sub = NULL;
char *result;
tevent_req_profile_get_name(profile, &req_name);
tevent_req_profile_get_start(profile, &start_location, &start);
timeval_str_buf(&start, false, true, &start_buf);
tevent_req_profile_get_stop(profile, &stop_location, &stop);
timeval_str_buf(&stop, false, true, &stop_buf);
diff = tevent_timeval_until(&start, &stop);
tevent_req_profile_get_status(profile, &pid, &state, &user_error);
switch(state) {
case TEVENT_REQ_INIT:
state_buf = "TEVENT_REQ_INIT";
break;
case TEVENT_REQ_IN_PROGRESS:
state_buf = "TEVENT_REQ_IN_PROGRESS";
break;
case TEVENT_REQ_DONE:
state_buf = "TEVENT_REQ_DONE";
break;
case TEVENT_REQ_USER_ERROR:
state_buf = "TEVENT_REQ_USER_ERROR";
break;
case TEVENT_REQ_TIMED_OUT:
state_buf = "TEVENT_REQ_TIMED_OUT";
break;
case TEVENT_REQ_NO_MEMORY:
state_buf = "TEVENT_REQ_NO_MEMORY";
break;
case TEVENT_REQ_RECEIVED:
state_buf = "TEVENT_REQ_RECEIVED";
break;
default:
state_buf = "unknown";
break;
}
result = talloc_asprintf_append_buffer(
*string,
"%*s[%s] %s [%s] %s [%s] [%ju.%.6ju] -> %s (%d %"PRIu64"))\n",
indent,
"",
req_name,
start_location,
start_buf.buf,
stop_location,
stop_buf.buf,
(uintmax_t)diff.tv_sec,
(uintmax_t)diff.tv_usec,
state_buf,
(int)state,
user_error);
if (result == NULL) {
return false;
}
*string = result;
indent += 1;
if (indent >= max_indent) {
return true;
}
for (sub = tevent_req_profile_get_subprofiles(profile);
sub != NULL;
sub = tevent_req_profile_next(sub)) {
bool ret;
ret = tevent_req_profile_string_internal(
sub,
indent,
max_indent,
string);
if (!ret) {
return false;
}
}
return true;
}
char *tevent_req_profile_string(TALLOC_CTX *mem_ctx,
const struct tevent_req_profile *profile,
unsigned indent,
unsigned max_indent)
{
char *result;
bool ret;
result = talloc_strdup(mem_ctx, "");
if (result == NULL) {
return NULL;
}
ret = tevent_req_profile_string_internal(
profile,
indent,
max_indent,
&result);
if (!ret) {
TALLOC_FREE(result);
return NULL;
}
return result;
}
static ssize_t tevent_req_profile_pack_one(
const struct tevent_req_profile *profile,
uint8_t *buf,
size_t buflen)
{
const char *req_name = NULL;
const char *start_location = NULL;
const char *stop_location = NULL;
struct timeval start_time, stop_time;
pid_t pid;
enum tevent_req_state state;
uint64_t user_error;
size_t pack_len, len;
int ret;
tevent_req_profile_get_name(profile, &req_name);
tevent_req_profile_get_start(profile, &start_location, &start_time);
tevent_req_profile_get_stop(profile, &stop_location, &stop_time);
tevent_req_profile_get_status(profile, &pid, &state, &user_error);
len = strlen(req_name)+1;
if (buflen >= len) {
memcpy(buf, req_name, len);
buf += len;
buflen -= len;
}
pack_len = len;
len = strlen(start_location)+1;
pack_len += len;
if (pack_len < len) {
return -1; /* overflow */
}
if (buflen >= len) {
memcpy(buf, start_location, len);
buf += len;
buflen -= len;
}
len = strlen(stop_location)+1;
pack_len += len;
if (pack_len < len) {
return -1; /* overflow */
}
if (buflen >= len) {
memcpy(buf, stop_location, len);
buf += len;
buflen -= len;
}
ret = snprintf((char *)buf,
buflen,
"%ju %ju %ju %ju %d %d %"PRIu64"",
(uintmax_t)start_time.tv_sec,
(uintmax_t)start_time.tv_usec,
(uintmax_t)stop_time.tv_sec,
(uintmax_t)stop_time.tv_usec,
(int)pid,
(int)state,
user_error);
if (ret < 0) {
return -1;
}
/*
* Take care of the trailing 0. No overflow check, this would
* be a VERY small number of bits for "int".
*/
ret += 1;
pack_len += ret;
return pack_len;
}
ssize_t tevent_req_profile_pack(
const struct tevent_req_profile *profile,
uint8_t *buf,
size_t buflen)
{
const struct tevent_req_profile *sub = NULL;
size_t num_sub;
ssize_t pack_len, profile_len;
int ret;
num_sub = 0;
pack_len = 0;
for (sub = tevent_req_profile_get_subprofiles(profile);
sub != NULL;
sub = tevent_req_profile_next(sub)) {
num_sub += 1;
}
ret = snprintf((char *)buf, buflen, "%zu ", num_sub);
if (ret < 0) {
return -1;
}
if (buflen > (size_t)ret) {
buf += ret;
buflen -= ret;
}
pack_len = ret;
profile_len = tevent_req_profile_pack_one(profile, buf, buflen);
if (profile_len == -1) {
return -1;
}
if (buflen >= (size_t)profile_len) {
buf += profile_len;
buflen -= profile_len;
}
pack_len += profile_len;
if (pack_len < profile_len) {
return -1; /* overflow */
}
for (sub = tevent_req_profile_get_subprofiles(profile);
sub != NULL;
sub = tevent_req_profile_next(sub)) {
profile_len = tevent_req_profile_pack(sub, buf, buflen);
if (profile_len == -1) {
return -1;
}
if (buflen >= (size_t)profile_len) {
buf += profile_len;
buflen -= profile_len;
}
pack_len += profile_len;
if (pack_len < profile_len) {
return -1; /* overflow */
}
}
return pack_len;
}
static bool parse_uintmax(const char *buf,
char delimiter,
uintmax_t *presult,
char **p_endptr)
{
uintmax_t result;
char *endptr;
result = strtoumax(buf, &endptr, 10);
if ((result == UINTMAX_MAX) && (errno == ERANGE)) {
return false;
}
if (*endptr != delimiter) {
return false;
}
*presult = result;
*p_endptr = endptr+1;
return true;
}
static ssize_t tevent_req_profile_unpack_one(
const uint8_t *buf,
size_t buflen,
struct tevent_req_profile *profile)
{
const char *orig_buf = (const char *)buf;
const char *req_name = NULL;
const char *start_location = NULL;
const char *stop_location = NULL;
uintmax_t start_sec, start_usec, stop_sec, stop_usec, pid, state;
uintmax_t user_error;
char *next = NULL;
size_t len;
bool ok;
if (buflen == 0) {
return -1;
}
if (buf[buflen-1] != '\0') {
return -1;
}
req_name = (const char *)buf;
len = strlen(req_name)+1;
buf += len;
buflen -= len;
if (buflen == 0) {
return -1;
}
start_location = (const char *)buf;
len = strlen(start_location)+1;
buf += len;
buflen -= len;
if (buflen == 0) {
return -1;
}
stop_location = (const char *)buf;
len = strlen(stop_location)+1;
buf += len;
buflen -= len;
if (buflen == 0) {
return -1;
}
ok = parse_uintmax((const char *)buf, ' ', &start_sec, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, ' ', &start_usec, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, ' ', &stop_sec, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, ' ', &stop_usec, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, ' ', &pid, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, ' ', &state, &next);
if (!ok) {
return -1;
}
ok = parse_uintmax(next, '\0', &user_error, &next);
if (!ok) {
return -1;
}
ok = tevent_req_profile_set_name(profile, req_name);
if (!ok) {
return -1;
}
ok = tevent_req_profile_set_start(
profile,
start_location,
(struct timeval){ .tv_sec=start_sec, .tv_usec=start_usec });
if (!ok) {
return -1;
}
ok = tevent_req_profile_set_stop(
profile,
stop_location,
(struct timeval){ .tv_sec=stop_sec, .tv_usec=stop_usec });
if (!ok) {
return -1;
}
tevent_req_profile_set_status(
profile,
pid,
(enum tevent_req_state)state,
user_error);
return next - orig_buf;
}
ssize_t tevent_req_profile_unpack(
const uint8_t *buf,
size_t buflen,
TALLOC_CTX *mem_ctx,
struct tevent_req_profile **p_profile)
{
const uint8_t *orig_buf = buf;
struct tevent_req_profile *profile = NULL;
uintmax_t i, num_subprofiles;
char *next = NULL;
bool ok;
ssize_t len;
errno = 0;
if (buf[buflen-1] != '\0') {
return -1;
}
ok = parse_uintmax((const char *)buf, ' ', &num_subprofiles, &next);
if (!ok) {
return -1;
}
len = (next - (const char *)buf);
buf += len;
buflen -= len;
profile = tevent_req_profile_create(mem_ctx);
if (profile == NULL) {
return -1;
}
len = tevent_req_profile_unpack_one(buf, buflen, profile);
if (len == -1) {
TALLOC_FREE(profile);
return -1;
}
buf += len;
buflen -= len;
for (i=0; i