forked from vitalif/vitastor
Rewrite upgrade-simple to C++
parent
a16263e88c
commit
40d8d65188
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/nodejs
|
||||
// DEPRECATED, DO NOT USE - use vitastor-disk prepare instead
|
||||
// systemd unit generator for hybrid (HDD+SSD) vitastor OSDs
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# DEPRECATED, DO NOT USE - use vitastor-disk prepare instead
|
||||
# Very simple systemd unit generator for vitastor-osd services
|
||||
# Not the final solution yet, mostly for tests
|
||||
# Copyright (c) Vitaliy Filippov, 2019+
|
||||
# License: MIT
|
||||
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
#!/usr/bin/node
|
||||
// Upgrade tool for OSD units generated with make-osd.sh and make-osd-hybrid.js
|
||||
|
||||
const fsp = require('fs').promises;
|
||||
const child_process = require('child_process');
|
||||
|
||||
upgrade_osd(process.argv[2]).catch(e =>
|
||||
{
|
||||
console.error(e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
async function upgrade_osd(unit)
|
||||
{
|
||||
if (!unit)
|
||||
throw new Error('USAGE: '+process.argv[0]+' '+process.argv[1]+' /etc/systemd/system/vitastor-osd<NUMBER>.service');
|
||||
let service_name = /^.*\/(vitastor-osd\d+)\.service$/.exec(unit);
|
||||
if (!service_name)
|
||||
throw new Error(unit+' is not a service named vitastor-osd<number>');
|
||||
service_name = service_name[1];
|
||||
// Parse the unit
|
||||
const text = await fsp.readFile(unit, { encoding: 'utf-8' });
|
||||
let cmd = /\nExecStart\s*=[^\n]+vitastor-osd\s*(([^\\\n&>\d]+|\\[ \t\r]*\n|\d[^>])+)/.exec(text);
|
||||
if (!cmd)
|
||||
throw new Error('Failed to extract ExecStart command from '+unit);
|
||||
cmd = cmd[1].replace(/\\[ \t\r]*\n/g, '').split(/\s+/);
|
||||
const options = {};
|
||||
for (let i = 0; i < cmd.length-1; i += 2)
|
||||
{
|
||||
if (cmd[i].substr(0, 2) == '--')
|
||||
options[cmd[i].substr(2)] = cmd[i+1];
|
||||
else
|
||||
i--;
|
||||
}
|
||||
if (!options['osd_num'] || !options['data_device'])
|
||||
throw new Error('osd_num or data_device are missing in '+unit);
|
||||
if (options['data_device'].substr(0, '/dev/disk/by-partuuid/'.length) != '/dev/disk/by-partuuid/' ||
|
||||
options['meta_device'] && options['meta_device'].substr(0, '/dev/disk/by-partuuid/'.length) != '/dev/disk/by-partuuid/' ||
|
||||
options['journal_device'] && options['journal_device'].substr(0, '/dev/disk/by-partuuid/'.length) != '/dev/disk/by-partuuid/')
|
||||
{
|
||||
throw new Error(
|
||||
'data_device, meta_device and journal_device must begin with'+
|
||||
' /dev/disk/by-partuuid/ i.e. they must be GPT partitions identified by UUIDs'
|
||||
);
|
||||
}
|
||||
// Stop and disable the service
|
||||
await system_or_die("systemctl disable --now "+service_name);
|
||||
const j_o = BigInt(options['journal_offset'] || 0);
|
||||
const m_o = BigInt(options['meta_offset'] || 0);
|
||||
const d_o = BigInt(options['data_offset'] || 0);
|
||||
const m_is_d = !options['meta_device'] || options['meta_device'] == options['data_device'];
|
||||
const j_is_m = !options['journal_device'] || options['journal_device'] == options['meta_device'];
|
||||
const j_is_d = (options['journal_device'] || options['meta_device'] || options['data_device']) == options['data_device'];
|
||||
if (d_o < 4096 || j_o < 4096 || m_o < 4096)
|
||||
{
|
||||
// Resize data
|
||||
let blk = BigInt(options['block_size'] || 128*1024);
|
||||
let resize = {};
|
||||
if (d_o < 4096 || m_is_d && m_o < 4096 && m_o < d_o || j_is_d && j_o < 4096 && j_o < d_o)
|
||||
{
|
||||
resize.new_data_offset = d_o+blk;
|
||||
if (m_is_d && m_o < d_o)
|
||||
resize.new_meta_offset = m_o+blk;
|
||||
if (j_is_d && j_o < d_o)
|
||||
resize.new_journal_offset = j_o+blk;
|
||||
}
|
||||
if (!m_is_d && m_o < 4096)
|
||||
{
|
||||
resize.new_meta_offset = m_o+4096n;
|
||||
if (j_is_m && m_o < j_o)
|
||||
resize.new_journal_offset = j_o+4096n;
|
||||
}
|
||||
if (!j_is_d && !j_is_m && j_o < 4096)
|
||||
resize.new_journal_offset = j_o+4096n;
|
||||
const resize_opts = Object.keys(resize).map(k => ` --${k} ${resize[k]}`).join('');
|
||||
const resize_cmd = 'vitastor-disk resize'+
|
||||
Object.keys(options).map(k => ` --${k} ${options[k]}`).join('')+resize_opts;
|
||||
await system_or_die(resize_cmd, { no_cmd_on_err: true });
|
||||
for (let k in resize)
|
||||
options[k.substr(4)] = ''+resize[k];
|
||||
}
|
||||
// Write superblock
|
||||
const sb = JSON.stringify(options);
|
||||
await system_or_die('vitastor-disk write-sb '+options['data_device'], { input: sb });
|
||||
if (!m_is_d)
|
||||
await system_or_die('vitastor-disk write-sb '+options['meta_device'], { input: sb });
|
||||
if (!j_is_d && !j_is_m)
|
||||
await system_or_die('vitastor-disk write-sb '+options['journal_device'], { input: sb });
|
||||
// Change partition type
|
||||
await fix_partition_type(options['data_device']);
|
||||
if (!m_is_d)
|
||||
await fix_partition_type(options['meta_device']);
|
||||
if (!j_is_d && !j_is_m)
|
||||
await fix_partition_type(options['journal_device']);
|
||||
// Enable the new unit
|
||||
await system_or_die("systemctl enable --now vitastor-osd@"+options['osd_num']);
|
||||
console.log('\nOK: Converted OSD '+options['osd_num']+' to the new scheme. The new service name is vitastor-osd@'+options['osd_num']);
|
||||
}
|
||||
|
||||
async function fix_partition_type(dev)
|
||||
{
|
||||
const uuid = dev.replace(/^.*\//, '').toLowerCase();
|
||||
const parent_dev = (await fsp.realpath(dev)).replace(/((\d)p|(\D))?\d+$/, '$2$3');
|
||||
const pt = JSON.parse(await system_or_die('sfdisk --dump '+parent_dev+' --json', { get_out: true })).partitiontable;
|
||||
let script = 'label: gpt\n\n';
|
||||
for (const part of pt.partitions)
|
||||
{
|
||||
if (part.uuid.toLowerCase() == uuid)
|
||||
part.type = 'e7009fac-a5a1-4d72-af72-53de13059903';
|
||||
script += part.node+': '+Object.keys(part).map(k => k == 'node' ? '' : k+'='+part[k]).filter(k => k).join(', ')+'\n';
|
||||
}
|
||||
await system_or_die('sfdisk --force '+parent_dev, { input: script, get_out: true });
|
||||
}
|
||||
|
||||
async function system_or_die(cmd, options = {})
|
||||
{
|
||||
let [ exitcode, stdout, stderr ] = await system(cmd, options);
|
||||
if (exitcode != 0)
|
||||
throw new Error((!options.no_cmd_on_err ? cmd : 'Command')+' failed'+(options.get_err ? ': '+stderr : ''));
|
||||
return stdout;
|
||||
}
|
||||
|
||||
async function system(cmd, options = {})
|
||||
{
|
||||
process.stderr.write('Running: '+cmd+(options.input != null ? " <<EOF\n"+options.input.replace(/\s*$/, '\n')+"EOF" : '')+'\n');
|
||||
const cp = child_process.spawn(cmd, {
|
||||
shell: true,
|
||||
stdio: [ 'pipe', options.get_out ? 'pipe' : 1, options.get_err ? 'pipe' : 1 ],
|
||||
});
|
||||
let stdout = '', stderr = '', finish_cb;
|
||||
if (options.get_out)
|
||||
cp.stdout.on('data', buf => stdout += buf.toString());
|
||||
if (options.get_err)
|
||||
cp.stderr.on('data', buf => stderr += buf.toString());
|
||||
cp.on('exit', () => finish_cb && finish_cb());
|
||||
if (options.input != null)
|
||||
cp.stdin.write(options.input);
|
||||
cp.stdin.end();
|
||||
if (cp.exitCode == null)
|
||||
await new Promise(ok => finish_cb = ok);
|
||||
return [ cp.exitCode, stdout, stderr ];
|
||||
}
|
|
@ -195,7 +195,7 @@ configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
|||
# vitastor-disk
|
||||
add_executable(vitastor-disk
|
||||
disk_tool.cpp disk_simple_offsets.cpp
|
||||
disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp disk_tool_udev.cpp disk_tool_utils.cpp
|
||||
disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp disk_tool_udev.cpp disk_tool_utils.cpp disk_tool_upgrade.cpp
|
||||
crc32c.c str_util.cpp ../json11/json11.cpp rw_blocking.cpp allocator.cpp ringloop.cpp blockstore_disk.cpp
|
||||
)
|
||||
target_link_libraries(vitastor-disk
|
||||
|
|
|
@ -18,6 +18,7 @@ static const char *help_text =
|
|||
" In the second form, you omit <devices> and pass --data_device, --journal_device\n"
|
||||
" and/or --meta_device which must be already existing partitions. In this case\n"
|
||||
" a single OSD is created.\n"
|
||||
" Requires `vitastor-cli`, `blkid` and `sfdisk` utilities.\n"
|
||||
" OPTIONS may include:\n"
|
||||
" --hybrid\n"
|
||||
" Prepare hybrid (HDD+SSD) OSDs using provided devices. SSDs will be used for\n"
|
||||
|
@ -45,6 +46,13 @@ static const char *help_text =
|
|||
" Use disks for OSD data even if they already have non-Vitastor partitions,\n"
|
||||
" but only if these take up no more than this percent of disk space.\n"
|
||||
"\n"
|
||||
"vitastor-disk upgrade-simple <UNIT_FILE|OSD_NUMBER>\n"
|
||||
" Upgrade an OSD created by old (0.7.1 and older) make-osd.sh or make-osd-hybrid.js scripts.\n"
|
||||
" Adds superblocks to OSD devices, disables old vitastor-osdN unit and replaces it with vitastor-osd@N.\n"
|
||||
" UNIT_FILE must be /etc/systemd/system/vitastor-osd<OSD_NUMBER>.service.\n"
|
||||
" Note that the procedure isn't atomic and may ruin OSD data if an error happens.\n"
|
||||
" Requires the `sfdisk` utility.\n"
|
||||
"\n"
|
||||
"vitastor-disk resize <ALL_OSD_PARAMETERS> <NEW_LAYOUT> [--iodepth 32]\n"
|
||||
" Resize data area and/or rewrite/move journal and metadata\n"
|
||||
" ALL_OSD_PARAMETERS must include all (at least all disk-related)\n"
|
||||
|
@ -263,6 +271,24 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
return self.pre_exec_osd(cmd[1]);
|
||||
}
|
||||
else if (!strcmp(cmd[0], "prepare"))
|
||||
{
|
||||
std::vector<std::string> devs;
|
||||
for (int i = 1; i < cmd.size(); i++)
|
||||
{
|
||||
devs.push_back(cmd[i]);
|
||||
}
|
||||
return self.prepare(devs);
|
||||
}
|
||||
else if (!strcmp(cmd[0], "upgrade-simple"))
|
||||
{
|
||||
if (cmd.size() != 2)
|
||||
{
|
||||
fprintf(stderr, "Exactly 1 OSD number or systemd unit path is required\n");
|
||||
return 1;
|
||||
}
|
||||
return self.upgrade_simple_unit(cmd[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
print_help(help_text, "vitastor-disk", cmd.size() > 1 ? cmd[1] : "", self.all);
|
||||
|
|
|
@ -120,6 +120,8 @@ struct disk_tool_t
|
|||
json11::Json add_partitions(vitastor_dev_info_t & devinfo, std::vector<std::string> sizes);
|
||||
std::vector<std::string> get_new_data_parts(vitastor_dev_info_t & dev, uint64_t osd_per_disk, uint64_t max_other_percent);
|
||||
int get_meta_partition(std::vector<vitastor_dev_info_t> & ssds, std::map<std::string, std::string> & options);
|
||||
|
||||
int upgrade_simple_unit(std::string unit);
|
||||
};
|
||||
|
||||
void disk_tool_simple_offsets(json11::Json cfg, bool json_output);
|
||||
|
|
|
@ -255,7 +255,7 @@ json11::Json disk_tool_t::add_partitions(vitastor_dev_info_t & devinfo, std::vec
|
|||
{
|
||||
script += "+ "+size+" "+std::string(VITASTOR_PART_TYPE)+"\n";
|
||||
}
|
||||
if (shell_exec({ "/sbin/sfdisk", devinfo.path }, script, NULL, NULL) != 0)
|
||||
if (shell_exec({ "/sbin/sfdisk", "--force", devinfo.path }, script, NULL, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to add %lu partition(s) with sfdisk\n", sizes.size());
|
||||
return {};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include <sys/file.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "disk_tool.h"
|
||||
|
@ -109,6 +110,11 @@ uint32_t disk_tool_t::write_osd_superblock(std::string device, json11::Json para
|
|||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
// Lock the file
|
||||
if (flock(fd, LOCK_EX|LOCK_NB) < 0)
|
||||
{
|
||||
fprintf(stderr, "Warning: Failed to lock %s with flock - udev autodetection may fail. Error: %s\n", device.c_str(), strerror(errno));
|
||||
}
|
||||
int r = write_blocking(fd, buf, buf_len);
|
||||
if (r < 0)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include <regex>
|
||||
#include "disk_tool.h"
|
||||
#include "str_util.h"
|
||||
|
||||
static std::map<std::string, std::string> read_vitastor_unit(std::string unit)
|
||||
{
|
||||
std::smatch m;
|
||||
if (unit == "" || !std::regex_match(unit, m, std::regex(".*/vitastor-osd\\d+\\.service")))
|
||||
{
|
||||
fprintf(stderr, "unit file name does not match <path>/vitastor-osd<NUMBER>.service\n");
|
||||
return {};
|
||||
}
|
||||
std::string text = read_file(unit);
|
||||
if (!std::regex_search(text, m, std::regex("\nExecStart\\s*=[^\n]+vitastor-osd\\s*(([^\\\\\n&>\\d]+|\\\\[ \t\r]*\n|\\d[^>])+)")))
|
||||
{
|
||||
fprintf(stderr, "Failed to extract ExecStart command from %s\n", unit.c_str());
|
||||
return {};
|
||||
}
|
||||
std::string cmd = trim(m[1]);
|
||||
cmd = str_replace(cmd, "\\\n", " ");
|
||||
std::string key;
|
||||
std::map<std::string, std::string> r;
|
||||
auto ns = std::regex("\\S+");
|
||||
for (auto it = std::sregex_token_iterator(cmd.begin(), cmd.end(), ns, 0), end = std::sregex_token_iterator();
|
||||
it != end; it++)
|
||||
{
|
||||
if (key == "" && ((std::string)(*it)).substr(0, 2) == "--")
|
||||
key = ((std::string)(*it)).substr(2);
|
||||
else if (key != "")
|
||||
{
|
||||
r[key] = *it;
|
||||
key = "";
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int fix_partition_type(std::string dev_by_uuid)
|
||||
{
|
||||
auto uuid = strtolower(dev_by_uuid.substr(dev_by_uuid.rfind('/')+1));
|
||||
std::string parent_dev = get_parent_device(realpath_str(dev_by_uuid, false));
|
||||
if (parent_dev == "")
|
||||
return 1;
|
||||
auto pt = read_parttable("/dev/"+parent_dev);
|
||||
if (pt.is_null())
|
||||
return 1;
|
||||
std::string script = "label: gpt\n\n";
|
||||
for (const auto & part: pt["partitions"].array_items())
|
||||
{
|
||||
bool this_part = (strtolower(part["uuid"].string_value()) == uuid);
|
||||
if (this_part && strtolower(part["type"].string_value()) == "e7009fac-a5a1-4d72-af72-53de13059903")
|
||||
{
|
||||
// Already correct type
|
||||
return 0;
|
||||
}
|
||||
script += part["node"].string_value()+": ";
|
||||
bool first = true;
|
||||
for (const auto & kv: part.object_items())
|
||||
{
|
||||
if (kv.first != "node")
|
||||
{
|
||||
script += (first ? "" : ", ")+kv.first+"="+
|
||||
(kv.first == "type" && this_part
|
||||
? "e7009fac-a5a1-4d72-af72-53de13059903"
|
||||
: (kv.second.is_string() ? kv.second.string_value() : kv.second.dump()));
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
script += "\n";
|
||||
}
|
||||
return shell_exec({ "/sbin/sfdisk", "--no-reread", "--force", "/dev/"+parent_dev }, script, NULL, NULL);
|
||||
}
|
||||
|
||||
int disk_tool_t::upgrade_simple_unit(std::string unit)
|
||||
{
|
||||
if (stoull_full(unit) != 0)
|
||||
{
|
||||
// OSD number
|
||||
unit = "/etc/systemd/system/vitastor-osd"+unit+".service";
|
||||
}
|
||||
auto options = read_vitastor_unit(unit);
|
||||
if (!options.size())
|
||||
return 1;
|
||||
if (!stoull_full(options["osd_num"], 10) || options["data_device"] == "")
|
||||
{
|
||||
fprintf(stderr, "osd_num or data_device are missing in %s\n", unit.c_str());
|
||||
return 1;
|
||||
}
|
||||
if (options["data_device"].substr(0, 22) != "/dev/disk/by-partuuid/" ||
|
||||
options["meta_device"] != "" && options["meta_device"].substr(0, 22) != "/dev/disk/by-partuuid/" ||
|
||||
options["journal_device"] != "" && options["journal_device"].substr(0, 22) != "/dev/disk/by-partuuid/")
|
||||
{
|
||||
fprintf(
|
||||
stderr, "data_device, meta_device and journal_device must begin with"
|
||||
" /dev/disk/by-partuuid/ i.e. they must be GPT partitions identified by UUIDs"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
// Stop and disable the service
|
||||
auto service_name = unit.substr(unit.rfind('/') + 1);
|
||||
if (shell_exec({ "systemctl", "disable", "--now", service_name }, "", NULL, NULL) != 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
uint64_t j_o = stoull_full(options["journal_offset"]);
|
||||
uint64_t m_o = stoull_full(options["meta_offset"]);
|
||||
uint64_t d_o = stoull_full(options["data_offset"]);
|
||||
bool m_is_d = options["meta_device"] == "" || options["meta_device"] == options["data_device"];
|
||||
bool j_is_m = options["journal_device"] == "" || options["journal_device"] == options["meta_device"];
|
||||
bool j_is_d = j_is_m && m_is_d || options["journal_device"] == options["data_device"];
|
||||
if (d_o < 4096 || j_o < 4096 || m_o < 4096)
|
||||
{
|
||||
// Resize data
|
||||
uint64_t blk = stoull_full(options["block_size"]);
|
||||
blk = blk ? blk : 128*1024;
|
||||
std::map<std::string, uint64_t> resize;
|
||||
if (d_o < 4096 || m_is_d && m_o < 4096 && m_o < d_o || j_is_d && j_o < 4096 && j_o < d_o)
|
||||
{
|
||||
resize["new_data_offset"] = d_o+blk;
|
||||
if (m_is_d && m_o < d_o)
|
||||
resize["new_meta_offset"] = m_o+blk;
|
||||
if (j_is_d && j_o < d_o)
|
||||
resize["new_journal_offset"] = j_o+blk;
|
||||
}
|
||||
if (!m_is_d && m_o < 4096)
|
||||
{
|
||||
resize["new_meta_offset"] = m_o+4096;
|
||||
if (j_is_m && m_o < j_o)
|
||||
resize["new_journal_offset"] = j_o+4096;
|
||||
}
|
||||
if (!j_is_d && !j_is_m && j_o < 4096)
|
||||
resize["new_journal_offset"] = j_o+4096;
|
||||
disk_tool_t resizer;
|
||||
resizer.options = options;
|
||||
for (auto & kv: resize)
|
||||
resizer.options[kv.first] = std::to_string(kv.second);
|
||||
if (resizer.resize_data() != 0)
|
||||
{
|
||||
// FIXME: Resize with backup or journal
|
||||
fprintf(
|
||||
stderr, "Failed to resize data to make space for the superblock\n"
|
||||
"Sorry, but your OSD may now be corrupted depending on what went wrong during resize :-(\n"
|
||||
"Please review the messages above and take action accordingly\n"
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
for (auto & kv: resize)
|
||||
options[kv.first.substr(4)] = std::to_string(kv.second);
|
||||
}
|
||||
// Write superblocks
|
||||
if (!write_osd_superblock(options["data_device"], options) ||
|
||||
(!m_is_d && !write_osd_superblock(options["meta_device"], options)) ||
|
||||
(!j_is_m && !j_is_d && !write_osd_superblock(options["journal_device"], options)))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// Change partition types
|
||||
if (fix_partition_type(options["data_device"]) != 0 ||
|
||||
(!m_is_d && fix_partition_type(options["meta_device"]) != 0) ||
|
||||
(!j_is_m && !j_is_d && fix_partition_type(options["journal_device"]) != 0))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// Enable the new unit
|
||||
if (shell_exec({ "systemctl", "enable", "--now", "vitastor-osd@"+options["osd_num"] }, "", NULL, NULL) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to enable systemd unit vitastor-osd@%s\n", options["osd_num"].c_str());
|
||||
return 1;
|
||||
}
|
||||
fprintf(
|
||||
stderr, "\nOK: Converted OSD %s to the new scheme. The new service name is vitastor-osd@%s\n",
|
||||
options["osd_num"].c_str(), options["osd_num"].c_str()
|
||||
);
|
||||
return 0;
|
||||
}
|
|
@ -148,13 +148,12 @@ int shell_exec(const std::vector<std::string> & cmd, const std::string & in, std
|
|||
close(child_stdout[1]);
|
||||
close(child_stderr[0]);
|
||||
close(child_stderr[1]);
|
||||
//char *argv[] = { (char*)"/bin/sh", (char*)"-c", (char*)cmd.c_str(), NULL };
|
||||
char *argv[cmd.size()+1];
|
||||
for (int i = 0; i < cmd.size(); i++)
|
||||
{
|
||||
argv[i] = (char*)cmd[i].c_str();
|
||||
}
|
||||
argv[cmd.size()-1] = NULL;
|
||||
argv[cmd.size()] = NULL;
|
||||
execvp(argv[0], argv);
|
||||
std::string full_cmd;
|
||||
for (int i = 0; i < cmd.size(); i++)
|
||||
|
|
Loading…
Reference in New Issue