Switch user at run time when program runs as root - c++

If some c++ program runs as root and i want to execute some of the commands with different user and read the output of that command and again switch back to root user so can someone guide me how to achieve that in linux OS & c++ ?
Below is the reference code i wrote. Can someone guide me which is correct or not ?
#include <iostream>
#include <stdio.h>
#include <string.h>
std::wstring PopenRead(const std::wstring &cmd)
{
std::wstring res;
std::string s_cmd(cmd.begin(), cmd.end());
FILE *f = popen((const char *)s_cmd.c_str(), "r");
if (f)
{
char buffer[1024];
int cnt;
int rc;
while ((cnt = fread(buffer, 1, 1024, f)) > 0)
{
buffer[cnt] = 0;
std::string s_val = std::string(buffer);
std::wstring wsTmp(s_val.begin(), s_val.end());
res += wsTmp;
}
rc = pclose(f);
std::wcout << "Output is: " << res << std::endl;
return res;
}
return L"";
}
int main()
{
std::wstring command = L"su test_user -c 'ls -ltr /home/test_user'";
std::wstring exec_res = PopenRead(command);
return 0;
}

I tried to do it using popen and sudo command as I'm using Ubuntu 18.04 LTS
Please find below code in C++
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
//To switch user as user
std::string switch_user_command("echo 'userpassword' | sudo -u user 2>&1");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe;
pipe = popen(switch_user_command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode1 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode1 << std::endl;
result.clear();
//To run ls command
std::string command("ls 2>&1");
pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode2 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode2 << std::endl;
//To run command as root/sudo
result.clear();
std::cout << "Trying to run ls as sudo .. " << std::endl;
std::string switch_root_command("echo 'sudopassword' | sudo -S ls 2>&1");
pipe = popen(switch_root_command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
//std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode3 = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode3 << std::endl;
return 0;
}
Please use below command to compile in g++
g++ prog.cpp -o prog -std=c++11
This produces below output (as I don't have any 'user' account)
Opening reading pipe
sudo: unknown user: user
sudo: unable to initialize policy plugin
256
1.cpp
2
2.cpp
0
Trying to run ls as sudo ..
1.cpp
2
2.cpp
0
abhi
0
I hope it helps!

Related

ifstream: /dev/stdin is not working the same as std::cin

For my formation, an exercise ask us to create a program similar to the linux 'cat' command.
So to read the file, i use an ifstream, and everything work fine for regular file.
But not when i try to open /dev/ files like /dev/stdin: the 'enter' is not detected and so, getline really exit only when the fd is being closed (with a CTRL-D).
The problem seems to be around how ifstream or getline handle reading, because with the regular 'read' function from libc, this problem is not to be seen.
Here is my code:
#include <iostream>
#include <string>
#include <fstream>
#include <errno.h>
#ifndef PROGRAM_NAME
# define PROGRAM_NAME "cato9tails"
#endif
int g_exitCode = 0;
void
displayErrno(std::string &file)
{
if (errno)
{
g_exitCode = 1;
std::cerr << PROGRAM_NAME << ": " << file << ": " << strerror(errno) << std::endl;
}
}
void
handleStream(std::string file, std::istream &stream)
{
std::string read;
stream.peek(); /* try to read: will set fail bit if it is a folder. */
if (!stream.good())
displayErrno(file);
while (stream.good())
{
std::getline(stream, read);
std::cout << read;
if (stream.eof())
break;
std::cout << std::endl;
}
}
int
main(int argc, char **argv)
{
if (argc == 1)
handleStream("", std::cin);
else
{
for (int index = 1; index < argc; index++)
{
errno = 0;
std::string file = std::string(argv[index]);
std::ifstream stream(file, std::ifstream::in);
if (stream.is_open())
{
handleStream(file, stream);
stream.close();
}
else
displayErrno(file);
}
}
return (g_exitCode);
}
We can only use method from libcpp.
I have search this problem for a long time, and i only find this post where they seems to have a very similar problem to me:
https://github.com/bigartm/bigartm/pull/258#issuecomment-128131871
But found no really usable solution from them.
I tried to do a very ugly solution but... well...:
bool
isUnixStdFile(std::string file)
{
return (file == "/dev/stdin" || file == "/dev/stdout" || file == "/dev/stderr"
|| file == "/dev/fd/0" || file == "/dev/fd/1" || file == "/dev/fd/2");
}
...
if (isUnixStdFile(file))
handleStream(file, std::cin);
else
{
std::ifstream stream(file, std::ifstream::in);
...
As you can see, a lot of files are missing, this can only be called a temporary solution.
Any help would be appreciated!
The following code worked for me to deal with /dev/fd files or when using shell substitute syntax:
std::ifstream stream(file_name);
std::cout << "Opening file '" << file_name << "'" << std::endl;
if (stream.fail() || !stream.good())
{
std::cout << "Error: Failed to open file '" << file_name << "'" << std::endl;
return false;
}
while (!stream.eof() && stream.good() && stream.peek() != EOF)
{
std::getline(stream, buffer);
std::cout << buffer << std::endl;
}
stream.close();
Basically std::getline() fails when content from the special file is not ready yet.

popen() writes output of command executed to cout

I am writing an application that needs to open another process and get its output. Everywhere I've read online says I have to use popen and read from the file.
But I can't read from it. The output of the command gets outputted into the console window of the calling application. Below is the code I am using. I added some prints to debug.
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
// some command that fails to execute properly.
std::string command("ls afskfksakfafkas");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode << std::endl;
return 0;
}
Reading is never actually printed to my cout and result is an empty string. I clearly see the output of the command in my terminal. If the command exits gracefully the behaviour is as expected. But I only capture the output for error cases.
Popen doesn't capture stderr only stdout. Redirecting stderr to stdout fixes the issue.
#include <string>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <array>
int main()
{
std::string command("ls afskfksakfafkas 2>&1");
std::array<char, 128> buffer;
std::string result;
std::cout << "Opening reading pipe" << std::endl;
FILE* pipe = popen(command.c_str(), "r");
if (!pipe)
{
std::cerr << "Couldn't start command." << std::endl;
return 0;
}
while (fgets(buffer.data(), 128, pipe) != NULL) {
std::cout << "Reading..." << std::endl;
result += buffer.data();
}
auto returnCode = pclose(pipe);
std::cout << result << std::endl;
std::cout << returnCode << std::endl;
return 0;
}
You have to add "2>&1" at the end of command string
command.append(" 2>&1");
there is a full example https://www.jeremymorgan.com/tutorials/c-programming/how-to-capture-the-output-of-a-linux-command-in-c/

state is "downloading", but torrent_file() returns NULL?

I recently tried to write a scraper capable of downloading 3.5 million
torrent files based on their magnet URL. I decided to start by hacking
an example from libtorrent's tutorial webpage, but while it works well
with just one torrent file, it fails segfaults in create_torrent() when
I try to download more than one file. Here's my code:
#include <thread>
#include <chrono>
#include <fstream>
#include <sstream>
#include <string>
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_status.hpp>
#include <libtorrent/torrent_info.hpp>
namespace lt = libtorrent;
using clk = std::chrono::steady_clock;
int torrents_left = 0;
int save_file(std::string const& filename, std::vector<char>& v)
{
FILE* f = std::fopen(filename.c_str(), "wb");
if (f == nullptr)
return -1;
int w = int(std::fwrite(&v[0], 1, v.size(), f));
std::fclose(f);
if (w < 0) return -1;
if (w != int(v.size())) return -3;
return 0;
}
void add_torrent_url(std::string url, lt::session& ses) {
// std::cerr << "DEBUG: Will download '" << url << "'" << std::endl;
lt::add_torrent_params atp;
atp.url = url;
atp.save_path = "."; // save in current dir
ses.async_add_torrent(atp);
torrents_left++;
}
void add_torrents_from_stdin(lt::session& ses) {
std::cerr << "DEBUG: reading stdin." << std::endl;
std::string url;
while(std::getline(std::cin, url)) {
add_torrent_url(url, ses);
}
std::cerr << "DEBUG: done reading stdin." << std::endl;
}
int main(int argc, char const* argv[])
{
lt::settings_pack pack;
pack.set_int(lt::settings_pack::alert_mask
, lt::alert::error_notification
| lt::alert::storage_notification
| lt::alert::status_notification);
lt::session ses(pack);
lt::add_torrent_params atp;
//add_torrent_url(argv[1]);
add_torrent_url("magnet:?xt=urn:btih:3E37CFE29B1049E03F858758A73EFD85BA170BE8", ses);
add_torrent_url("magnet:?xt=urn:btih:8FCDE178E3F9A24EA40856826C4E8A625A931B73", ses);
//add_torrents_from_stdin(ses);
// this is the handle we'll set once we get the notification of it being
// added
lt::torrent_handle h;
for (;;) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
for (lt::alert const* a : alerts) {
if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
h = at->handle;
}
// if we receive the finished alert or an error, we're done
if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
std::cout << "torrent finished or error." << std::endl;
goto done;
}
if (lt::alert_cast<lt::torrent_error_alert>(a)) {
std::cout << a->message() << std::endl;
goto done;
}
if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty()) continue;
// we only have a single torrent, so we know which one
// the status is for
lt::torrent_status const& s = st->status[0];
if (s.state == lt::torrent_status::downloading)
{
std::cout << "Hi!" << std::endl;
std::shared_ptr<const lt::torrent_info> ti = h.torrent_file();
if (ti == 0) {
std::cerr << "ERROR: ti == NULL" << std::endl;
goto done;
}
ses.remove_torrent(h, lt::session::delete_files);
lt::create_torrent new_torrent(*ti);
std::vector<char> out;
lt::bencode(std::back_inserter(out), new_torrent.generate());
std::stringstream ss;
ss << "downloaded/" << (*ti).info_hash() << ".torrent";
save_file(ss.str(), out);
h.pause();
torrents_left--;
std::cerr << "DEBUG: Done (" << torrents_left << " left): " << (*ti).info_hash() << std::endl;
if (torrents_left == 0)
goto done;
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// ask the session to post a state_update_alert, to update our
// state output for the torrent
ses.post_torrent_updates();
}
done:
{}
}
I suspect it's related to this part:
// we only have a single torrent, so we know which one
// the status is for
lt::torrent_status const& s = st->status[0];
But according to my debugger, when torrent_file() gives NULL, st->status[] only contains one element anyway.
What's happening here? How do I fix it?
It looks like I made wrong assumptions about what "h" points to in the example. Here's a diff that fixes the code in question:
--- scrape_rasterbar.cpp 2017-01-07 21:00:39.565636805 +0100
+++ scrape_rasterbar_old.cpp 2017-01-07 21:05:53.339718098 +0100
## -1,4 +1,3 ##
-#include <iostream>
#include <thread>
#include <chrono>
#include <fstream>
## -94,17 +93,18 ##
if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty()) continue;
- for (auto &s : st->status) {
// we only have a single torrent, so we know which one
// the status is for
+ lt::torrent_status const& s = st->status[0];
if (s.state == lt::torrent_status::downloading)
{
- std::shared_ptr<const lt::torrent_info> ti = s.handle.torrent_file();
+ std::cout << "Hi!" << std::endl;
+ std::shared_ptr<const lt::torrent_info> ti = h.torrent_file();
if (ti == 0) {
std::cerr << "ERROR: ti == NULL" << std::endl;
goto done;
}
- ses.remove_torrent(s.handle, lt::session::delete_files);
+ ses.remove_torrent(h, lt::session::delete_files);
lt::create_torrent new_torrent(*ti);
std::vector<char> out;
lt::bencode(std::back_inserter(out), new_torrent.generate());
## -112,7 +112,7 ##
std::stringstream ss;
ss << "downloaded/" << (*ti).info_hash() << ".torrent";
save_file(ss.str(), out);
- s.handle.pause();
+ h.pause();
torrents_left--;
std::cerr << "DEBUG: Done (" << torrents_left << " left): " << (*ti).info_hash() << std::endl;
if (torrents_left == 0)
## -120,7 +120,6 ##
}
}
}
- }
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// ask the session to post a state_update_alert, to update our

Makefile error: boost/asio.hpp: No such file or directory (Windows)

I have a C++ program, that compiles fine on Linux. But when I tried to compile it on Windows, I got: fatal error: boost/asio.hpp: No such file or directory.
I don't understand how is that possible, since I have few more boost includes (<boost/thread.hpp> and <boost/locale.hpp>) and they work fine. I also checked in my boost library, and thread.hpp, locale.hpp and asio.hpp are all there.
If that matters, on Linux I installed boost with the following commands (and it works there):
sudo apt-get install libboost-all-dev
sudo apt-get install aptitude
aptitude search boost
And this is my code:
Client.cpp:
#include "../include/ConnectionHandler.h"
#include <stdlib.h>
#include <boost/thread.hpp>
#include <boost/locale.hpp>
//A method used to read messages from the console (stdin) and send them to the server using a ConnectionHandler
void sendMessage(ConnectionHandler *connectionHandler){ // handles stdin
if (connectionHandler->isConnected()){
while (1){
const short bufsize = 1024;
char buf[bufsize];
std::cin.getline(buf, bufsize);
std::string line(buf);
if (!connectionHandler->sendLine(line)) { break; }
if (line=="QUIT"){
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
if (!connectionHandler->isConnected()) { break; }
}
}
}
}
//A method used to read messages from the socket (using the ConnectionHandler)
void recieveMessage(ConnectionHandler *connectionHandler){ // handles socket
while (1){
std::string answer;
if (!connectionHandler->getLine(answer)) {
std::cout << "Disconnected.\nExiting...\n" << std::endl;
break;
}
int len=answer.length();
answer.resize(len-1);
std::cout << answer << std::endl << std::endl; // double line drops so it will be easier to notice between messages
if (answer == "SYSMSG QUIT ACCEPTED") {
std::cout << "Disconnected.\nExiting...\n" << std::endl;
break;
}
}
(*connectionHandler).close();
}
int main (int argc, char *argv[]) {
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " host port" << std::endl << std::endl;
return -1;
}
std::string host = argv[1];
unsigned short port_ = atoi(argv[2]);
ConnectionHandler *connectionHandler_= new ConnectionHandler(host,port_);
if (!(connectionHandler_)->connect()) {
std::cerr << "Cannot connect to " << host << ":" << port_ << std::endl;
delete connectionHandler_;
return 1;
}
boost::thread sender(&sendMessage,connectionHandler_); // a different thread to handle stdin
boost::thread reciever(&recieveMessage,connectionHandler_); // a different thread to handle the socket
reciever.join();
sender.join();
delete connectionHandler_;
}
ConnectionHandler.h:
#ifndef CONNECTION_HANDLER__
#define CONNECTION_HANDLER__
#include <string>
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class ConnectionHandler {
private:
const std::string host_;
const short port_;
boost::asio::io_service io_service_; // Provides core I/O functionality
tcp::socket socket_;
bool isConnected_;
public:
ConnectionHandler(std::string host, short port);
virtual ~ConnectionHandler();
bool connect();
bool getBytes(char bytes[], unsigned int bytesToRead);
bool sendBytes(const char bytes[], int bytesToWrite);
bool getLine(std::string& line);
bool sendLine(std::string& line);
bool getFrameAscii(std::string& frame, char delimiter);
bool sendFrameAscii(const std::string& frame, char delimiter);
void close();
bool isConnected();
};
#endif
ConnectionHandler.cpp:
#include "../include/ConnectionHandler.h"
using boost::asio::ip::tcp;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::string;
ConnectionHandler::ConnectionHandler(string host, short port): host_(host), port_(port), io_service_(), socket_(io_service_), isConnected_(false){}
ConnectionHandler::~ConnectionHandler() { close(); }
bool ConnectionHandler::connect() {
std::cout << "Starting connect to " << host_ << ":" << port_ << std::endl;
try {
tcp::endpoint endpoint(boost::asio::ip::address::from_string(host_), port_); // the server endpoint
boost::system::error_code error;
socket_.connect(endpoint, error);
if (error) { throw boost::system::system_error(error); }
else{
std::cout << "Connection to " << host_ << ":" << port_ << " Successfully made!" << std::endl;
isConnected_ = true;
}
}
catch (std::exception& e) {
std::cerr << "Connection failed (Error: " << e.what() << ')' << std::endl;
return false;
}
return true;
}
bool ConnectionHandler::getBytes(char bytes[], unsigned int bytesToRead) {
size_t tmp = 0;
boost::system::error_code error;
try {
while (!error && bytesToRead > tmp ) {
tmp += socket_.read_some(boost::asio::buffer(bytes+tmp, bytesToRead-tmp), error);
}
if(error) { throw boost::system::system_error(error); }
}
catch (std::exception& e) {
std::cerr << "recv failed (Error: " << e.what() << ')' << std::endl;
return false;
}
return true;
}
bool ConnectionHandler::sendBytes(const char bytes[], int bytesToWrite) {
int tmp = 0;
boost::system::error_code error;
try {
while (!error && bytesToWrite > tmp ) {
tmp += socket_.write_some(boost::asio::buffer(bytes + tmp, bytesToWrite - tmp), error);
}
if(error) { throw boost::system::system_error(error); }
}
catch (std::exception& e) {
std::cerr << "recv failed (Error: " << e.what() << ')' << std::endl;
return false;
}
return true;
}
bool ConnectionHandler::getLine(std::string& line) { return getFrameAscii(line, '\n'); }
bool ConnectionHandler::sendLine(std::string& line) { return sendFrameAscii(line, '\n'); }
bool ConnectionHandler::getFrameAscii(std::string& frame, char delimiter) {
char ch;
do{
if(!getBytes(&ch, 1)) { return false; }
frame.append(1, ch);
}while (delimiter != ch);
return true;
}
bool ConnectionHandler::sendFrameAscii(const std::string& frame, char delimiter) {
bool result=sendBytes(frame.c_str(),frame.length());
if(!result) return false;
return sendBytes(&delimiter,1);
}
// Close down the connection properly.
void ConnectionHandler::close() {
try{
socket_.close();
isConnected_ = false;
}
catch (...) { std::cout << "closing failed: connection already closed" << std::endl; }
}
bool ConnectionHandler::isConnected(){ return isConnected_; }
makefile:
# All Targets
all: client
# Tool invocations
# Executable "client" depends on the files Client.o, ConnectionHandler.o.
client: bin/Client.o bin/ConnectionHandler.o
#echo 'Building target: client'
#echo 'Invoking: C++ Linker'
g++ -o bin/client bin/Client.o bin/ConnectionHandler.o -lboost_system -lboost_locale -lboost_thread
#echo 'Finished building target: client'
#echo ' '
# Depends on the source and header files
bin/Client.o: src/Client.cpp
g++ -g -Wall -c -Linclude -I/usr/local/boost/1.57.0/include/boost -o bin/Client.o src/Client.cpp
# Depends on the source and header files
bin/ConnectionHandler.o: src/ConnectionHandler.cpp
g++ -g -Wall -Weffc++ -c -Linclude -o bin/ConnectionHandler.o src/ConnectionHandler.cpp
.PHONY: clean
#Clean the build directory
clean:
rm -f bin/*
How can I solve this error?

How to read asynchronous file I/O on Linux linking with Oracle Client 12c libraries?

The following code works just fine when linking with Oracle client 11g but if I compile the same linking with Oracle 12c libraries I receive the error EINVAL Invalid argument - errno 22.
#include <sys/types.h>
#include <aio.h>
#include <fcntl.h>
#include <errno.h>
#include <iostream>
using namespace std;
const int SIZE_TO_READ = 100;
int main()
{
// open the file
int file = open("blah.txt", O_RDONLY, 0);
if (file == -1)
{
cout << "Unable to open file!" << endl;
return 1;
}
// create the buffer
char* buffer = new char[SIZE_TO_READ];
// create the control block structure
aiocb cb;
memset(&cb, 0, sizeof(aiocb));
cb.aio_nbytes = SIZE_TO_READ;
cb.aio_fildes = file;
cb.aio_offset = 0;
cb.aio_buf = buffer;
// read!
if (aio_read(&cb) == -1)
{
cout << "Unable to create request!" << endl;
close(file);
}
cout << "Request enqueued!" << endl;
// wait until the request has finished
while(aio_error(&cb) == EINPROGRESS)
{
cout << "Working..." << endl;
}
// success?
int numBytes = aio_return(&cb);
if (numBytes != -1)
cout << "Success!" << endl;
else
cout << "Error!" << endl;
// now clean up
delete[] buffer;
close(file);
return 0;
}
Then to compile I first set the env variables
export ICLIBHOME=/u01/oracle/product/Linux/2.6/x86_64/clients/12.1.0.2/64bit/client/lib
export LD_LIBRARY_PATH=${ICLIBHOME}
Afterwards I compile and execute my compiled file but I get the error
g++ myaio.cpp -o myaio -lrt -L${ICLIBHOME} -lclntsh
./myaio
Unable to create request 22