/*
* Copyright (c) 2007 Hypertriton, Inc.
* 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 AUTHOR 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "machctld.h"
#include "prog.h"
#include "pathnames.h"
#include "mkpath.h"
static char *
parse_path(NS_Command *cmd, const char *key)
{
char *path = NS_CommandString(cmd, key);
if (path[0] == '/' || strstr(path, "..") != NULL) {
AG_SetError("%s: bad program path: `%s'", key, path);
return (NULL);
}
return (path);
}
static char *
parse_name(NS_Command *cmd, const char *key)
{
char *name = NS_CommandString(cmd, key);
const char *p;
for (p = &key[0]; *p != '\0'; p++) {
if (*p == '/' || *p == '.' || *p == ' ') {
AG_SetError("%s: bad name: %s", key, name);
return (NULL);
}
}
if ((p - &key[0]) > RCSINFO_NAME_MAX) {
AG_SetError("%s: name too large", key);
return (NULL);
}
return (name);
}
static char *
parse_msg(NS_Command *cmd, const char *key)
{
char *name = NS_CommandString(cmd, key);
const char *p;
for (p = &key[0]; *p != '\0'; p++) {
if (!isprint(*p)) {
AG_SetError("%s: non-printable character", key);
return (NULL);
}
}
if ((p - &key[0]) > RCSINFO_MSG_MAX) {
AG_SetError("%s: name too large", key);
return (NULL);
}
return (name);
}
static size_t
parse_size(NS_Command *cmd, const char *key)
{
long lval;
if (NS_CommandLong(cmd, key, &lval) == -1)
return (0);
/* Agar signatures are at least 8 bytes. */
if (lval < 8) {
AG_SetError("%s: data file is too small (%lu)", key, lval);
return (0);
}
return ((size_t)lval);
}
int
prog_commit(NS_Server *ns, NS_Command *cmd, void *p)
{
char buf[BUFSIZ];
char filepath[MAXPATHLEN];
char *progname, *objname, *objtype;
size_t nread = 0, size;
int fd;
if ((progname = parse_path(cmd, "prog-name")) == NULL ||
(size = parse_size(cmd, "prog-size")) == 0) {
return (-1);
}
if (mkpath(progname, 0755) == -1) {
return (-1);
}
Strlcpy(filepath, progname, sizeof(filepath));
Strlcat(filepath, "/", sizeof(filepath));
Strlcat(filepath, progname, sizeof(filepath));
Strlcat(filepath, ".Prog", sizeof(filepath));
if ((fd = open(filepath, O_CREAT|O_WRONLY|O_EXCL, 0644)) == -1) {
AG_SetError("%s: %s", filepath, strerror(errno));
NS_Log(NS_ERR, "%s", AG_GetError());
return (-1);
}
NS_Log(NS_INFO, "Importing %s (%lu bytes)", filepath,
(u_long)size);
NS_Message(ns, 0, "Go ahead");
while (nread < size) {
size_t csize, rv;
if ((csize = (size - nread)) > sizeof(buf))
csize = sizeof(buf);
if ((rv = fread(buf, 1, csize, stdin)) < csize) {
AG_SetError("incomplete chunk");
goto fail;
}
if (write(fd, buf, rv) < rv) {
AG_SetError("write error");
goto fail;
}
nread += rv;
}
close(fd);
NS_Message(ns, 0, "Program OK");
return (0);
fail:
close(fd);
NS_Message(ns, 1, "Import failed (%s)", AG_GetError());
NS_Log(NS_ERR, "Import failed: %s: %s", filepath, AG_GetError());
return (-1);
}
int
prog_fetch(NS_Server *ns, NS_Command *cmd, void *p)
{
char buf[BUFSIZ];
char filepath[MAXPATHLEN];
char *dirpath, *progname;
off_t len;
ssize_t rv;
int fd;
if ((progname = parse_name(cmd, "prog-name")) == NULL) {
return (-1);
}
Strlcpy(filepath, progname, sizeof(filepath));
Strlcat(filepath, "/", sizeof(filepath));
Strlcat(filepath, progname, sizeof(filepath));
Strlcat(filepath, ".Prog", sizeof(filepath));
NS_Log(NS_INFO, "Sending program %s", filepath);
if ((fd = open(filepath, O_RDONLY|O_EXCL)) == -1) {
AG_SetError("%s: %s", filepath, strerror(errno));
NS_Log(NS_ERR, "%s", AG_GetError());
return (-1);
}
if ((len = lseek(fd, 0, SEEK_END)) == -1) {
AG_SetError("%s: cannot seek end", filepath);
goto fail_com;
}
if (lseek(fd, 0, SEEK_SET) == -1) {
AG_SetError("%s: cannot seek set", filepath);
goto fail_com;
}
NS_BeginData(ns, (size_t)len);
while ((rv = read(fd, buf, sizeof(buf))) > 0) {
if (NS_Data(ns, buf, rv) < rv) {
AG_SetError("%s: error writing to socket", filepath);
goto fail_bin;
}
}
if (rv < 0) {
AG_SetError("%s: read error", filepath);
goto fail_bin;
}
close(fd);
NS_EndData(ns);
return (0);
fail_bin:
NS_Log(NS_ERR, "%s", AG_GetError());
NS_EndData(ns);
fail_com:
close(fd);
return (-1);
}
int
prog_list(NS_Server *ns, NS_Command *cmd, void *p)
{
struct stat sb;
struct dirent *dent;
DIR *dir;
if ((dir = opendir(_PATH_DATA)) == NULL) {
AG_SetError("%s: %s", _PATH_DATA, strerror(errno));
NS_Log(NS_CRIT, "%s", AG_GetError());
return (-1);
}
NS_BeginList(ns);
while ((dent = readdir(dir)) != NULL) {
if (dent->d_name[0] == '.' ||
stat(dent->d_name, &sb) == -1) {
continue;
}
if ((sb.st_mode & S_IFDIR) == S_IFDIR) {
NS_ListString(ns, "%s:%lu", dent->d_name,
(unsigned long)sb.st_size);
}
}
closedir(dir);
NS_EndList(ns);
return (0);
}