How do I use a variable in an argument line? - c++

I am using SOCI to make MYSQL connections from my QT4 C++ application. I could hard code a mysql server IP, username, and password but I would rather pass this information in as a variable instead.
For example, I would like to do something like this, but there are no functions that accept these kinds of parameters:
char host [9];
sprintf(host, "192.x.x.x");
session sql(mysql, "host=%s db=dbname user=user password=password", host);
I also tried:
string host = "192.x.x.x";
session sql(mysql,
boost::format("host=%1% db=dbname user=user password='pword'",
%host));
But this also fails.
UPDATE: I was able to solve the problem like so:
QString connection;
connection.append("host=" + host +
" db=dbname user=" + uname +
" password=" + pword);
session sql(mysql, connection.toLatin1().data());

Try using QString's arg function.
session sql(mysql, QString("host=%1 db=%2 user=%3 password=%4").arg(sqlserver).arg(dbname).arg(user).arg(password));
I know it's ugly, and Qt should come up with a prettier solution, but it works.

You can't just arbitrarily pass any function that expects a string one that contains formatting and then a variable amount of whatever after it. C++ simply doesn't work that way. You need to build your string before passing it in. How? The other two answers provide ways even if they get the calling semantics of the session constructor wrong (that's your part to figure out). You might also consider use of boost::format or, if you want to be evil, sprintf.

Use string streams.
Then you can construct the string and concatenate variables as you go. Call .str().c_str() at the end.

Related

How to compact an Access database using the ODBC .accdb driver

Am trying to determine how to compact an Access database using the .accdb ODBC driver. Note that I'm aware of how to accomplish this using the old .mdb driver, essentially something like this:
const TCHAR *Driver = TEXT("Microsoft Access Driver (*.mdb)");
TCHAR *DatabasePath = GetRegDBPath() ;
_stprintf_s(Attr, sizeof(Attr)/sizeof(TCHAR), TEXT("COMPACT_DB=%s%s %s%s General"), DatabasePath,
DBName,
DatabasePath,
DBName ) ;
Ret = SQLConfigDataSource(hPar,
ODBC_ADD_DSN,
Driver,
Attr);
However substituting the Driver string with Microsoft Access Driver (.mdb, .accdb), the call fails and calling *SQLInstallerError *to obtain error information comes back with (the rather unhelpful) message: Driver's ConfigDSN, ConfigDriver, or ConfigTranslator failed.
I've tried varations on the above syntax, using a different output database path, omitting the General parameter, all yield the same result.
This link suggests it can't be done but I'm sceptical because it's possible to do it from the ODBC Administrator Panel. Additionally looking through the ACEODBC.DLL for string content shows the presence of the COMPACT_DB strings:
jon#monolith:~/temp$ strings -t x -e l ACEODBC.DLL | grep -i compact
395f0 COMPACT_DB
39978 COMPACT_DB=%s
All suggests it should be possibe if I knew what the correct syntax is.
As far as I know, no, ODBC was never able to compact a access database.
your posted code does a printf (simple outputs some text), and that certainly would not and does not do a compact database. the next line of code "might" C + R the datbase, but that code is not posted.
eg:
SQLConfigDataSource
And I suppose it also possible that the TEXT() method used in above might have some code, but then again, you have to provide what that TEXT() method does then.
From what I can find?
You have to create a "com" object instance of the Access database engine (JET for mdb files, and ACE for accDB files).

Ssh command from Qt

I have a problem with ssh in my Qt application. I need to run a command which removes a file on a remote server. I tried to use a QProcess class to achieve it. So I pass "ssh" as a name of command and necessary args. In common it looks like this:
QStringList params;
params.append(" user#" + ::host +
" \"rm /tmp/" + ::fileName + "\"");
d->impDelProcess->start("ssh", params);
But after all it keeps asking a password, though I generated ssh keys and copied a public key to the remote server. Moreover, when I run the command above in the terminal like this:
ssh user#host "rm /path/fileName"
it works perfect. The file is deleted and no password is asked. So, the problem is somwhere in QProcess. Is any way to get rid of asking a password? Thank you!
Those are separate arguments, when you use string list. Try this:
params.append("user#" + ::host");
params.append("rm /tmp/" + ::fileName);
That will make Qt pass two arguments for ssh, the login string and the command to execute at remote host.
Important note! Above assumes ::filename does not contain spaces or anything else nasty!. You can get around spaces with this:
params.append("rm '/tmp/" + ::fileName + "'");
But it won't help against wild cards or .. in path... Imagine if file name was ../home/user/* for example... So that is better be trusted input, or you need to sanitize it (but that is beyond scope of this answer).
What you do in the question code is to construct a single argument, equivalent to this shell command line:
ssh 'user#host "rm /path/filename"'

C++ how can I add a linux user to a group programatically

is it possible to add a user on Linux to a group, programatically when I start my program? I am using C++. I know it can be done with the shell, but I need to do it in my C++ program, however I don't know what to use.
You could take 2 approaches:
Use the system() call to execute a shell command to do the work.
Directly modify the /etc/group file. It should be fairly easy to locate the line that contains the group you wish to add to, and then append the user you want to add on the end (making sure to add a comma if there's already a group member).
Given the choice, I'd probably use the first option from the above.
EDIT: As #DevSolar pointed out in his comment, this answer is assuming that your system is using standard local authentication using passwd/shadow/group files in /etc. If your system is NOT using local auth, then that's a completely different ballgame.
Jared Sutton's two options are valid, but I'd like to point out that you don't have to implement editing /etc/group yourself if you use an existing library.
I know of at least busybox, which has implemented this. There's a function defined in libbb/update_passwd.c
int update_passwd(const char *filename, // /etc/group
const char *name, // username
const char *new_passwd, // nullptr
const char *member) // groupname
If you do want to implement it yourself, then you can check out how their function works.
This task is solved by many configuration management software. For example in Salt:
def adduser(name, username):
on_redhat_5 = __grains__.get('os_family') == 'RedHat' and __grains__.get('osmajorrelease') == '5'
if __grains__['kernel'] == 'Linux':
if on_redhat_5:
cmd = 'gpasswd -a {0} {1}'.format(username, name)
else:
cmd = 'gpasswd --add {0} {1}'.format(username, name)
else:
cmd = 'usermod -G {0} {1}'.format(name, username)
retcode = __salt__['cmd.retcode'](cmd, python_shell=False)
return not retcode
As you can see, operating system commands are used. Which one depends on what operating system it is :)

formatted string to stream

I'm sure I'm not the first one to encounter this situation, still, couldn't find anything specific neither here, nor generally.
I'm using an old logger, that uses the printf formatting... e.g.:
LOG_ERROR("Error Code: %d. Additional data %s", error.ID, error.serialize());
And want to move to a stream-style logger. e.g.:
LOG_ERROR("Error Code: " << error.ID << "Additional data: " << error.serialize());
Naturally, the code base is big and I want to automate this. I think I can come up with a script that does it, but it looks not so trivial... I'm sure there's a pretty simple way.
Thanks!
I would not dare to automate that task.
You may rename all old LOG_ERROR to (something like) LOG_ERROR_FORMAT, let LOG_ERROR_FORMAT create a string and pass that to the new LOG_ERROR.
If you really want to go for it, I suggest a look at some printf implementation to get the parsing right

C++: How to escape user input for safe system calls?

On a Linux platform, I have C++ code that goes like this:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
std::string command;
command = "mkdir " + myDir;
if (system(command.c_str()) != 0) {
return 1;
}
// continue....
Is passing user input to a system() call safe at all?
Should the user input be escaped / sanitized?
How?
How could the above code be exploited for malicious purposes?
Thanks.
Just don't use system. Prefer execl.
execl ("/bin/mkdir", "mkdir", myDir, (char *)0);
That way, myDir is always passed as a single argument to mkdir, and the shell isn't involved. Note that you need to fork if you use this method.
But if this is not just an example, you should use the mkdir C function:
mkdir(myDir, someMode);
Using system() call with command line parameters without sanitizing the input can be highly insecure.
The potential security threat could be a user passing the following as directory name
somedir ; rm -rf /
To prevent this , use a mixture of the following
use getopt to ensure your input is
sanitized
sanitize the input
use execl instead of system to execute
the command
The best option would be to use all three
Further to Matthew's answer, don't spawn a shell process unless you absolutely need it. If you use a fork/execl combination, individual parameters will never be parsed so don't need to be escaped. Beware of null characters however which will still prematurely terminate the parameter (this is not a security problem in some cases).
I assume mkdir is just an example, as mkdir can trivially be called from C++ much more easily than these subprocess suggestions.
Reviving this ancient question as I ran into the same problem and the top answers, based on fork() + execl(), weren't working for me. (They create a separate process, whereas I wanted to use async to launch the command in a thread and have the system call stay in-process to share state more easily.) So I'll give an alternative solution.
It's not usually safe to pass user input as-is, especially if the utility is designed to be sudo'd; in order to sanitize it, instead of composing the string to be executed yourself, use environment variables, which the shell has built-in escape mechanisms for.
For your example:
// ...
std::string myDir;
myDir = argv[1]; // myDir is initialized using user input from the command line.
setenv("MY_DIR", myDir, 1);
if (system("mkdir \"${MY_DIR}\"") != 0) {
return 1;
}
// continue....