Program
Source code:
Gist: Better for online visualization.
Online Compiler:
This code is a reverse polish notation (RPN) interactive interpreter for math computation based on a subset of the old Forth programming language.
The RPN notation may look award, but it is very practical and convenient for manual calculations and implementation of parsers, evaluators and interpreters. As a result, those RPN features have inspired some programming languages like:
- Forth programming language
- PostScript
- Factor programming language
- RPL - HP Calculators - programming language
- dc (computer program) - Wikipedia
- HP ( Hewlett-Packard ) Calculators RPN Calculators such as:
- HP 48 series - family of scientific and engineering calculators which uses RPN notation.
- HP-12C - Financial calculator with RPN notation.
Notes:
- This math evaluator can be extended for implementing some embedded scripting language.
- Modern C++ Features used:
- Dynamic Polymorphism, aka subtyping polymorphism.
- Template metaprogramming for the generic stack container.
- Smart Pointers for resource management.
- C++11 Lambdas and std::function
- C++ exceptions for error handling.
Compile:
$ clang++ rpn-calculator.cpp -o rpn-calculator.bin -Wall -Wextra -std=c++1z -g
Running:
$ ./rpn-calculator.bin
EXPR+> 10 20 +
stack: 30
EXPR+> 50 -
stack: -20
EXPR+> 10 *
stack: -200
EXPR+> 300 +
stack: 100
stack: 100
EXPR+> M_PI 2 / sin
stack: 100 1
EXPR+> M_PI 2 / cos
stack: 100 1 6.12323e-17
EXPR+> M_LN10 exp
stack: 100 1 6.12323e-17 10
EXPR+> clear
stack:
EXPR+> 30 dup
stack: 30 30
EXPR+> 40 dup
stack: 30 30 40 40
EXPR+> clear
stack:
EXPR+> 30 dup *
stack: 900
EXPR+> 40 dup *
stack: 900 1600
EXPR+> +
stack: 2500
EXPR+> sqrt
stack: 50
EXPR+> 30 dup * 40 dup * + sqrt
stack: 50 50
EXPR+>
EXPR+> quit
Exiting REPL OK.
This sample program is an Posix daemon encapsulated in a class which runs in background, even when the terminal is closed and logs to syslog a hypothetical commodity price simulated by a random number.
The program was tested on Linux, however it may work in any other compatible Posix operating system such as BSD, MacOSX and so on.
Code:
Modern C++ Features used:
- std::functions is used for callback and inject behavior in the PosixDaemon class.
- C++11 auto syntax for functions and variables.
- Deleted copy constructor and copy-assignment operator.
- C++11 random number facilities.
Posix and Linux APIs used:
- syslog
- close() system calls
- fork()
- umaks()
See:
Parts
Class PosixDaemon:
- Encapsulates the U-nix and Posix daemon.
class PosixDaemon{
public:
using Action = std::function<bool ()>;
PosixDaemon(std::string path, std::string pidfile, Action action)
: m_path(std::move(path)),
m_pidfile(std::move(pidfile)),
m_action(action) { }
~PosixDaemon(){}
// Disable copy constructor in order to forbid copy
PosixDaemon(const PosixDaemon&) = delete;
// Disable copy-assignment operator to make the class non-copiable.
PosixDaemon& operator= (const PosixDaemon&) = delete;
auto run() -> void {
// Make child process
pid_t child_pid = fork();
if(child_pid < 0){
std::cerr << "Error: failed to fork this process." << "\n";
return;
}
if(child_pid > 0){
std::cout << "Process ID of child process = " << child_pid << "\n";
return;
}
// Umask file mode
::umask(0);
// Set new session
pid_t sid = ::setsid();
if(sid < 0)
return;
//------ Code of Forked Process ---------//
// Set path of forked process (daemon)
::chdir(m_path.c_str());
// Check whether there is a running process of this program
auto fs = std::ifstream(m_pidfile);
if(fs.good()){
int pid;
fs >> pid;
std::cerr << " [LOG] Kill process of PID = " << pid << "\n";
::kill(-pid, SIGTERM);
fs.close();
}
auto fo = std::ofstream(m_pidfile);
if(fo.good()){
int pid = ::getpid();
std::cerr << "Child PID = " << pid << "\n";
fo << pid << std::flush;
} else {
std::cerr << " [LOG] Error: could not open PID file " << m_pidfile << "\n";
return;
}
// Close stdin, stdout and stderr file descriptors.
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
while(m_action());
}
private:
std::string m_path;
std::string m_pidfile;
Action m_action;
// Action m_onExit;
};
Main function:
- The PosixDaemon’s constructor takes the following parameters: the path where the daemon process will run, the path to PID file used to ensure that only a single instance is running and a lambda function which contains the code that will be in the daemon process.
std::random_device rdng;
auto randomGen = std::bind(
std::uniform_real_distribution<double>(10.0, 60.0),
std::default_random_engine(rdng())
);
setlogmask (LOG_UPTO (LOG_INFO));
openlog ("price-service", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
PosixDaemon daemon{
"/",
"/tmp/price-server.pid",
[&randomGen](){
// Action executed in the child process (daemon)
std::stringstream ss;
ss << std::setprecision(3) << std::fixed;
ss << "Price = " << randomGen() << " path = " << getcwd(nullptr, 0) ;
syslog (LOG_INFO, "%s", ss.str().c_str());
// 1 seconds delay
std::this_thread::sleep_for(std::chrono::seconds(1));
return true;
}
};
daemon.run();
Compiling:
$ clang++ posix-daemon.cpp -o posix-daemon.bin -g -std=c++1z -Wall -Wextra && ./posix-daemon.bin
Running:
- If there is already a process of this program running, it is killed and a new daemon is launched.
$ ./posix-daemon.bin
Process ID of child process = 6022
[LOG] Kill process of PID = 4795
Child PID = 6022
See the daemon output with syslog:
$ journalctl -f -t price-service
-- Logs begin at Wed 2018-10-03 16:00:02 -03. --
Nov 16 15:40:39 localhost.localdomain price-service[6022]: Price = 14.357 path = /
Nov 16 15:40:40 localhost.localdomain price-service[6022]: Price = 22.924 path = /
Nov 16 15:40:41 localhost.localdomain price-service[6022]: Price = 51.256 path = /
Nov 16 15:40:42 localhost.localdomain price-service[6022]: Price = 55.588 path = /
Nov 16 15:40:43 localhost.localdomain price-service[6022]: Price = 43.370 path = /
Nov 16 15:40:44 localhost.localdomain price-service[6022]: Price = 31.471 path = /
Nov 16 15:40:45 localhost.localdomain price-service[6022]: Price = 54.759 path = /
... ... ... ... ... ... ... ... ...
Stop daemon:
$ kill 6022
This is a command line tool which can bind a server or client socket to a login shell, thus allowing remote access in a similar way to the old telnet protocol.
Modern C++ Features used:
- Enum classes for modelling and handling socket errors.
- C++11 auto keyword for functions, member functions and variables.
- Lambda functions for handling daemon process, server’s client handling loop and so on.
- =delete annotation for copy-constructor and copy-assignment operator.
Building:
$ clang++ bsd-socket-shell.cpp -o bsd-socket-shell.bin -lpthread -g -std=c++1z -Wall -Wextra
Show usage
$ ./bsd-socket-shell.bin
Usage:
: Success
./bsd-socket-shell.bin echo [port] [host]
./bsd-socket-shell.bin server [port] [host] [shell]
./bsd-socket-shell.bin client [port] [host] [shell]
Runnign as echo server:
- Runing as server: Listen all addresses at port 9070
$ ./bsd-socket-shell.bin echo 9070 0.0.0.0
Running ECHO server.
Waiting incoming connections ...: Success
[TRACE] Connection received.: Success
line = hello world from server
line = testing server
line =
line = new line to server
[TRACE] Socket closed OK: Success
[TRACE] Connection received.: Success
- Client side: netcat.
$ rlwrap nc -v localhost 9070
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Connection to ::1 failed: Connection refused.
Ncat: Trying next address...
Ncat: Connected to 127.0.0.1:9070.
=> Connected from: 127.0.0.1:56336
hello world from server
=> ECHO hello world from server
testing server
=> ECHO testing server
=> ECHO
new line to server
=> ECHO new line to server
$ rlwrap nc -v localhost 9070
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Connection to ::1 failed: Connection refused.
Ncat: Trying next address...
Ncat: Connected to 127.0.0.1:9070.
=> Connected from: 127.0.0.1:56340
Running as a telnet server for remote access
- Server: bsd-socket-shell.bin
- When the server starts, the process is forked (copied) as a daemon process which can run even if the terminal is closed. Then, the initial process asks the user to hit return for stopping the daemon process.
$ ./bsd-socket-shell.bin server 9070 0.0.0.0 /bin/sh
[LOG] Running daemon: Success
Process ID of child process = 17763
[LOG] Daemon started OK.: Success
===> Enter RETURN for kill the daemon process or type CTRL+C to let it run.
[TRACE] Running daemon - PID = 17763
===> Daemon stopped OK.
- Client: netcat
- User types commands in the client side and they processed at the server side.
$ rlwrap nc -v localhost 9070
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Connection to ::1 failed: Connection refused.
Ncat: Trying next address...
Ncat: Connected to 127.0.0.1:9070.
=> Connected from: 127.0.0.1:56366
pwd
/
uname -a
Linux localhost.localdomain 4.16.3-301.fc28.x86_64 ... ....
cd /etc
whoami
archbox
uptime
05:31:23 up 1 day, 15:03, 1 user, load average: 1.85, 1.68, 1.65
# It is possible to connect and reconnect many times.
$ rlwrap nc -v localhost 9070
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Connection to ::1 failed: Connection refused.
Ncat: Trying next address...
Ncat: Connected to 127.0.0.1:9070.
=> Connected from: 127.0.0.1:56372
pwd
/
ls
bin
boot
dev
etc
home
lib
lib64
lost+found
media
mnt
Running as a telnet server as client
- Server: netcat
- User types command from this side.
$ rlwrap nc -v -l 0.0.0.0 9060
Ncat: Version 7.60 ( https://nmap.org/ncat )
Ncat: Generating a temporary 1024-bit RSA key. Use --ssl-key and --ssl-cert to use a permanent one.
Ncat: SHA-1 fingerprint: A753 5AC9 F6DF EC54 FE34 EF19 FC4B 02B7 79B5 BA30
Ncat: Listening on 0.0.0.0:9060
Ncat: Connection from 127.0.0.1.
Ncat: Connection from 127.0.0.1:57248.
=> Reverse shell connected from: 127.0.0.1:9060
whoami
archbox
pwd
/
free -m
total used free shared buff/cache available
Mem: 15931 7682 654 469 7594 7444
Swap: 19659 1 19658
which clang++
/usr/lib64/ccache/clang++
# Exit remote shell
exit
# Connect again.
$ rlwrap nc -l 0.0.0.0 9060
=> Reverse shell connected from: 127.0.0.1:9060
which clang++
/usr/lib64/ccache/clang++
- Client: If the client-side terminal is closed or the user types CTRL+C, the client daemon process will keep running in background and trying to connect to the server, even if the server-side is shutdown.
$ ./bsd-socket-shell.bin client 9060 127.0.0.1 /bin/sh
Process ID of child process = 18231
[LOG] Client daemon started OK.: Success
===> Enter RETURN for kill the daemon process or type CTRL+C to let it run.
[TRACE] Running daemon - PID = 18231
Command line tool for inspectig binary files with the following features:
- Identify files by magic number
- Extract byte array at a given position.
- Extract: 8 bits, 32 bits, 64 bits, integers signed or unsigned at given position.
- File: src/sample-programs/binfo.cpp
- Gist: binfo.cpp
Build:
$ clang++ binreader.cpp -o binreader.bin -g -std=c++1z -Wall -Wextra
Display usage:
$ ./binfo.bin
Binary Info => Extract information from binary files.
Usage: ./binreader info [FILE]
Usage: ./binreader get [TYPE] [OFFSET] [FILE]
Usage: ./binreader bytes-char [SIZE] [OFFSET] [FILE]
Usage: ./binreader bytes-hex [SIZE] [OFFSET] [FILE]
Identify file formats by magic number (sequence of bytes in at the beginning).
$ ./binfo.bin info /usr/bin/bash
Maximum size = 16
.N/A => Unix Executable - ELF
Bytes at 0x00 ==> 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
$ ./binfo.bin info ~/account\ mind\ map.pdf
Maximum size = 16
.pdf PDF - Portable Document File
Bytes at 0x00 ==> 25 50 44 46 2d 31 2e 34 0a 25 c3 a4 c3 bc c3 b6
$ ./binfo.bin info ~/Documents/logos/6904820-nasdaq-stock-market-new-york.jpg
Maximum size = 16
JPEG Image - Extensions .jpg, .jpeg, .jpe, .jif, .jfif, .jfi
Bytes at 0x00 ==> ff d8 ff e0 00 10 4a 46 49 46 00 01 02 01 01 2c
Show first 10 bytes of files as characters:
$ ./binfo.bin bytes-char 10 0x00 /usr/bin/bash
RESULT ==> \0x7f E L F \0x02 \0x01 \0x01 \0x00 \0x00 \0x00
$ ./binfo.bin bytes-char 10 0x00 ~/Fedora-Workstation-Live-x86_64-28-1.1.iso
RESULT ==> E R \0x08 \0x00 \0x00 \0x00 \0x90 \0x90 \0x00 \0x00
Extracting information at specific positions of a binary file (layout here ELF Format) Unix native executable.
- “Magic number” identifying bytes 0x7F followed by ELF
- offset: 0x00
- 0x7F ELF (0x7F 0x45 0x4C 0x46)
e_ident[EI_OSABI]
(size = 1 byte, offset = 0x07)- contains the sytem ABI.
e_machine
(size = 1 byte, offset = 0x12)- Processor or instruction set.
e_entry
(size = 8 bytes [64 bits], offfset = 0x18)- Memory address of entry point where the process starts executing.
# Magic number
$ ./binfo.bin bytes-char 4 0x00 /usr/bin/bash
RESULT ==> \0x7f E L F
$ ./binfo.bin bytes-hex 4 0x00 /usr/bin/bash
RESULT ==> 7f 45 4c 46
# System ABI => Result 0x00 or SystemV ABI
$ ./binfo.bin get ui1 0x07 /usr/bin/bash
RESULT ==> {hex = 0x0 ; dec = 0}
# Processor architechture =>> 0x3E or x86-64 (64 bits)
$ ./binfo.bin get ui1 0x12 /usr/bin/bash
RESULT ==> {hex = 0x3E ; dec = 62}
# Entry point
$ ./binfo.bin get ui8 0x18 /usr/bin/bash
RESULT ==> {hex = 0x2E4B0 ; dec = 189616}