sending an email from a C/C++ program in linux - c++

I would like to send an email to my gmail account everytime my simulation ends. I have tried searching the web and found sendEmail but it is timing-out. If anyone could point me out to a package or link that they tried I would be thankful.
Thanks

You could invoke your local MTA directly using popen() and feed it RFC822-compliant text.
#include <stdio.h>
#include <string.h>
#include <errno.h>
int sendmail(const char *to, const char *from, const char *subject, const char *message)
{
int retval = -1;
FILE *mailpipe = popen("/usr/lib/sendmail -t", "w");
if (mailpipe != NULL) {
fprintf(mailpipe, "To: %s\n", to);
fprintf(mailpipe, "From: %s\n", from);
fprintf(mailpipe, "Subject: %s\n\n", subject);
fwrite(message, 1, strlen(message), mailpipe);
fwrite(".\n", 1, 2, mailpipe);
pclose(mailpipe);
retval = 0;
}
else {
perror("Failed to invoke sendmail");
}
return retval;
}
main(int argc, char** argv)
{
if (argc == 5) {
sendmail(argv[1], argv[2], argv[3], argv[4]);
}
}

libESMTP seems to be what you are looking for. It's very well documented and also seems to be under active development (last Release Candidate is from mid-January 2012). It also supports SSL and various authentication protocols.
There are example applications in the source package.

Both VMime and libcurl are good libraries for email sending (and more).

I like the answer of trojanfoe above, BUT in my case I needed to turn on an email sending agent.. an MTA to enable linux to send emails - I have found exim4 to be a relatively simple MTA to get working and that trojanfoe's program works very nicely with it.
to get it to work I used (on a debian type system in a virtual box (crunchbang linux))
sudo apt-get install exim
sudo apt-get install mailutils
I configured exim4 with
sudo dpkg-reconfigure exim4-config
and I told the computer about my normal (remote) email address with
sudo emacs /etc/email-addresses
hope this might be useful as these were the steps I found worked to get my linux system sending email with trojanfoe's email program above

Do a fork exec and pipe the body to a program like sendmail/mail:
#include <string>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
using std::string;
static const int READEND = 0;
static const int WRITEEND = 1;
int sendEmail(const string& to, const string& subject, const string& body) {
int p2cFd[2];
int ret = pipe(p2cFd);
if (ret) {
return ret;
}
pid_t child_pid = fork();
if (child_pid < 0) {
close(p2cFd[READEND]);
close(p2cFd[WRITEEND]);
return child_pid;
}
else if (!child_pid) {
dup2(p2cFd[READEND], READEND);
close(p2cFd[READEND]);
close(p2cFd[WRITEEND]);
execlp("mail", "mail", "-s", subject.c_str(), to.c_str(), NULL);
exit(EXIT_FAILURE);
}
close(p2cFd[READEND]);
ret = write(p2cFd[WRITEEND], body.c_str(), body.size());
if (ret < 0) {
return ret;
}
close(p2cFd[WRITEEND]);
if (waitpid(child_pid, &ret, 0) == -1) {
return ret;
}
return 0;
}
int main() {
return sendEmail("email#hostname.com", "Subject", "Body");
}

Related

RTMP_Init “Segmentation fault”

I am using librtmp to stream video.I install librtmp in ubuntu 16.04 with "sudo apt-get librtmp-dev".I successfully compiled the code,but I got a "Segmentation fault" in RTMP_Init().Here is my rtmp_test code:
main function:
int main(int argc,char *argv[]){
printf("11111\n");
if (RTMP264_Connect("rtmp://**********") == TRUE){
printf("connect successful");
}
printf("22222222\n");
return 0;
}
RTMP264_Connect function:
int RTMP264_Connect(const char * url){
printf("~~~~~~~1\n");
RTMP_Init(m_Rtmp);
printf("~~~~~~~2\n");
if(RTMP_SetupURL(m_Rtmp,(char*)url) == FALSE){
RTMP_Free(m_Rtmp);
return FALSE;
}
printf("~~~~~~~3\n");
RTMP_EnableWrite(m_Rtmp);
if(RTMP_Connect(m_Rtmp,NULL)==FALSE){
RTMP_Free(m_Rtmp);
return FALSE;
}
When I run the code,I got this error:
11111
aaaa
~~~~~~~1
Segmentation fault
So I'm sure the problem is in RTMP_init function.I saw someone on the Internet saying that the reason may be that socket is not initialized. I found a socket initialization code:
int InitSockets()
{
#ifdef WIN32
WORD version;
WSADATA wsaData;
version = MAKEWORD(1, 1);
return (WSAStartup(version, &wsaData) == 0);
#endif
}
void CleanupSockets()
{
#ifdef WIN32
WSACleanup();
#endif
}
But this is for Windows system. If I run on Ubuntu, I will report an error. And I don't know if my problem has something to do with socket not being initialized.Could someone tell me how to fix my problem,thanks!
According to the documentation:
The basic interaction is as follows. A session handle is created using RTMP_Alloc() and initialized using RTMP_Init().
As you haven't called RTMP_Alloc this is presumably the cause of the segfault

FastCGI with C++ other ways to start than spawn-fcgi

I'm currently working on a project that involves FastCGI and C++. Now I found the official FCGI Library. I tried out the echo example.
/*
* echo.c --
*
* Produce a page containing all FastCGI inputs
*
*
* Copyright (c) 1996 Open Market, Inc.
*
* See the file "LICENSE.TERMS" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef lint
static const char rcsid[] = "$Id: echo.c,v 1.1.1.1 2001/04/25 00:43:49 robs Exp $";
#endif /* not lint */
#include "fcgi_stdio.h"
#include <stdlib.h>
extern char **environ;
void PrintEnv(char *label, char **envp)
{
printf("%s:<br>\n<pre>\n", label);
for(; *envp != NULL; envp++) {
printf("%s\n", *envp);
}
printf("</pre><p>\n");
}
void main ()
{
char **initialEnv = environ;
int count = 0;
while(FCGI_Accept() >= 0) {
char *contentLength = getenv("CONTENT_LENGTH");
int len;
printf("Content-type: text/html\r\n"
"\r\n"
"<title>FastCGI echo</title>"
"<h1>FastCGI echo</h1>\n"
"Request number %d <p>\n", ++count);
if(contentLength != NULL) {
len = strtod(contentLength, NULL);
} else {
len = 0;
}
if(len <= 0) {
printf("No data from standard input.<p>\n");
} else {
int i, ch;
printf("Standard input:<br>\n<pre>\n");
for(i = 0; i < len; i++) {
if((ch = getchar()) < 0) {
printf("Error: Not enough bytes received "
"on standard input<p>\n");
break;
}
putchar(ch);
}
printf("\n</pre><p>\n");
}
PrintEnv("Request environment", environ);
PrintEnv("Initial environment", initialEnv);
} /* while */
}
I start this script with the command spawn-fcgi -p 8000 -n hello.
But is it also possible to just start the program xy without the spawn-fcgi. Do you know a good example, or a documentation?
thanks for your answer
The spawn-fcgi command opens a TCP connection for you and starts listening on the specified port (8000 in your case). It forwards the request coming in on the TCP connection to your application's stdin. It also forwards your writes to the stdout back to the TCP connection.
You can create the connection yourself using FCGX_OpenSocket() call and then pass the returned socket to FCGX_InitRequest(). After that you can go for the loop using FCGX_Accept_r() instead of FCGI_Accept()!
BTW: there is another tool that many people use instead of spawn-fcgi - supervisor. In addition to managing the connection for you, it also monitors your process. So, if your process crashes because of some wierd request, it re-launches your application!

Generate machine-specific key for Mac

On Windows, we generate a PC-specific unique key used to tie a license to a PC. It's a C++ app using wxWidgets, which is theoretically cross-platform compatible but not been maintained on the Mac side. We use some Win32-specific code for generating a key... how might I do something comparable on the Mac?
Looking more into whitelionV and blahdiblah's asnwers, I found this useful page:
Accessing the system serial number programmatically
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
// Returns the serial number as a CFString.
// It is the caller's responsibility to release the returned CFString when done with it.
void CopySerialNumber(CFStringRef *serialNumber)
{
if (serialNumber != NULL) {
*serialNumber = NULL;
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
if (platformExpert) {
CFTypeRef serialNumberAsCFString =
IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
if (serialNumberAsCFString) {
*serialNumber = serialNumberAsCFString;
}
IOObjectRelease(platformExpert);
}
}
}
Accessing the built-in MAC address programmatically
#include <stdio.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/network/IOEthernetInterface.h>
#include <IOKit/network/IONetworkInterface.h>
#include <IOKit/network/IOEthernetController.h>
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices);
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize);
static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)
{
kern_return_t kernResult;
CFMutableDictionaryRef matchingDict;
CFMutableDictionaryRef propertyMatchDict;
matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);
if (NULL == matchingDict) {
printf("IOServiceMatching returned a NULL dictionary.\n");
}
else {
propertyMatchDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (NULL == propertyMatchDict) {
printf("CFDictionaryCreateMutable returned a NULL dictionary.\n");
}
else {
CFDictionarySetValue(matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
CFRelease(propertyMatchDict);
}
}
kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, matchingServices);
if (KERN_SUCCESS != kernResult) {
printf("IOServiceGetMatchingServices returned 0x%08x\n", kernResult);
}
return kernResult;
}
static kern_return_t GetMACAddress(io_iterator_t intfIterator, UInt8 *MACAddress, UInt8 bufferSize)
{
io_object_t intfService;
io_object_t controllerService;
kern_return_t kernResult = KERN_FAILURE;
if (bufferSize < kIOEthernetAddressSize) {
return kernResult;
}
bzero(MACAddress, bufferSize);
while ((intfService = IOIteratorNext(intfIterator)))
{
CFTypeRef MACAddressAsCFData;
kernResult = IORegistryEntryGetParentEntry(intfService,
kIOServicePlane,
&controllerService);
if (KERN_SUCCESS != kernResult) {
printf("IORegistryEntryGetParentEntry returned 0x%08x\n", kernResult);
}
else {
MACAddressAsCFData = IORegistryEntryCreateCFProperty(controllerService,
CFSTR(kIOMACAddress),
kCFAllocatorDefault,
0);
if (MACAddressAsCFData) {
CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr
CFDataGetBytes(MACAddressAsCFData, CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
CFRelease(MACAddressAsCFData);
}
(void) IOObjectRelease(controllerService);
}
(void) IOObjectRelease(intfService);
}
return kernResult;
}
int main(int argc, char *argv[])
{
kern_return_t kernResult = KERN_SUCCESS;
io_iterator_t intfIterator;
UInt8 MACAddress[kIOEthernetAddressSize];
kernResult = FindEthernetInterfaces(&intfIterator);
if (KERN_SUCCESS != kernResult) {
printf("FindEthernetInterfaces returned 0x%08x\n", kernResult);
}
else {
kernResult = GetMACAddress(intfIterator, MACAddress, sizeof(MACAddress));
if (KERN_SUCCESS != kernResult) {
printf("GetMACAddress returned 0x%08x\n", kernResult);
}
else {
printf("This system's built-in MAC address is %02x:%02x:%02x:%02x:%02x:%02x.\n",
MACAddress[0], MACAddress[1], MACAddress[2], MACAddress[3], MACAddress[4], MACAddress[5]);
}
}
(void) IOObjectRelease(intfIterator); // Release the iterator.
return kernResult;
}
While MAC is on the face of it probably preferable as being more predictable, they warn that:
Netbooting introduces a wrinkle with systems with multiple built-in
Ethernet ports. The primary Ethernet port on these systems is the one
that is connected to the NetBoot server. This means that a search for
the primary port may return either of the built-in MAC addresses
depending on which port was used for netbooting. Note that "built-in"
does not include Ethernet ports that reside on an expansion card.
It concerns me this might mean you don't always get the same value back?
You could just call system_profiler and look for "Serial Number"
/usr/sbin/system_profiler | grep "Serial Number (system)"
There might well be a programmatic way to get the same information, but I don't know it offhand.
To uniquely identify any machine you could try to use the MAC address. The process, although not trivial, its quite simple. There are a lot of cross platform open source libraries.
In fact you could try this Apple dev example

C++ get linux distribution name\version

According to the question " How to get Linux distribution name and version? ", to get the linux distro name and version, this works:
lsb_release -a
On my system, it shows the needed output:
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 9.10
Release: 9.10
Codename: karmic
Now, to get this info in C++, Qt4's QProcess would be a great option but since I am developing without Qt using std c++, I need to know how to get this info in standard C++, i.e. the stdout of the process, and also a way to parse the info.
Uptil now I am trying to use code from here but am stuck on function read().
You can simply use the function:
int uname(struct utsname *buf);
by including the header
#include <sys/utsname.h>
It already returns the name & version as a part of the structure:
struct utsname
{
char sysname[]; /* Operating system name (e.g., "Linux") */
char nodename[]; /* Name within "some implementation-defined network" */
char release[]; /* OS release (e.g., "2.6.28") */
char version[]; /* OS version */
char machine[]; /* Hardware identifier */
#ifdef _GNU_SOURCE
char domainname[]; /* NIS or YP domain name */
#endif
};
Am I missing something?
For recent linux distros you can use following to get the OS info. The output is pretty standard and can be parsed using following spec:
https://www.freedesktop.org/software/systemd/man/os-release.html
cat /etc/os-release
Sample outputs:
NAME=Fedora
VERSION="27 (Twenty Seven)"
ID=fedora
VERSION_ID=27
PRETTY_NAME="Fedora 27 (Twenty Seven)"
NAME="Ubuntu"
VERSION="16.04.4 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.4 LTS"
VERSION_ID="16.04"
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
ID_LIKE=archlinux
ANSI_COLOR="0;36"
Got it from cplusplus.com forums, a simple call GetSystemOutput("/usr/bin/lsb_release -a") works.
char* GetSystemOutput(char* cmd){
int buff_size = 32;
char* buff = new char[buff_size];
char* ret = NULL;
string str = "";
int fd[2];
int old_fd[3];
pipe(fd);
old_fd[0] = dup(STDIN_FILENO);
old_fd[1] = dup(STDOUT_FILENO);
old_fd[2] = dup(STDERR_FILENO);
int pid = fork();
switch(pid){
case 0:
close(fd[0]);
close(STDOUT_FILENO);
close(STDERR_FILENO);
dup2(fd[1], STDOUT_FILENO);
dup2(fd[1], STDERR_FILENO);
system(cmd);
//execlp((const char*)cmd, cmd,0);
close (fd[1]);
exit(0);
break;
case -1:
cerr << "GetSystemOutput/fork() error\n" << endl;
exit(1);
default:
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
int rc = 1;
while (rc > 0){
rc = read(fd[0], buff, buff_size);
str.append(buff, rc);
//memset(buff, 0, buff_size);
}
ret = new char [strlen((char*)str.c_str())];
strcpy(ret, (char*)str.c_str());
waitpid(pid, NULL, 0);
close(fd[0]);
}
dup2(STDIN_FILENO, old_fd[0]);
dup2(STDOUT_FILENO, old_fd[1]);
dup2(STDERR_FILENO, old_fd[2]);
return ret;
}
int writepipe[2];
if (pipe(writepipe) < 0) {
perror("pipe");
return 1;
}
int ret = fork();
if (ret < 0) {
perror("fork");
return 1;
}
else if (ret == 0) // child process
{
dup2(writepipe[1],1); // set writepipe[1] as stdout
// close fds
close(writepipe[0]);
close(writepipe[1]);
execlp("lsb_release","lsb_release","-a",NULL); //TODO: Error checking
}
else // parent process
{
int status;
waitpid(ret,&status,0); //TODO: Error checking
//do what you need
//read output of lsb_release from writepipe[0]
}
It works for me
There are files named /etc/version and /etc/release which have information like whether you're using Ubuntu or Fedora, etc. (which is what the OP clarified his question to be).
Personally I like the uname solution posted by #Alok Slav, but in case it helps someone who needs to use a command-line utility to get the info, consider using popen.

AIO on OS X vs Linux - why it doesn't work on Mac OS X 10.6

My question is really simple. Why the code below does work on Linux, and doesn't on Mac OS X 10.6.2 Snow Leopard.
To compile save the file to aio.cc, and compile with g++ aio.cc -o aio -lrt on Linux, and g++ aio.cc -o aio on Mac OS X. I'm using Mac OS X 10.6.2 for testing on a Mac, and Linux kernel 2.6 for testing on Linux.
The failure I see on OS X is aio_write fails with -1 and sets errno to EAGAIN, which simply means "Resource temporarily unavailable". Why is that?
extern "C" {
#include <aio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#include <signal.h>
}
#include <cassert>
#include <string>
#include <iostream>
using namespace std;
static void
aio_completion_handler(int signo, siginfo_t *info, void *context)
{
using namespace std;
cout << "BLAH" << endl;
}
int main()
{
int err;
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_port = htons(1234);
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_family = PF_INET;
int sd = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sd == -1) {
assert(!"socket() failed");
}
const struct sockaddr *saddr = reinterpret_cast<const struct sockaddr *>(&sin);
err = ::connect(sd, saddr, sizeof(struct sockaddr));
if (err == -1) {
perror(NULL);
assert(!"connect() failed");
}
struct aiocb *aio = new aiocb();
memset(aio, 0, sizeof(struct aiocb));
char *buf = new char[3];
buf[0] = 'a';
buf[1] = 'b';
buf[2] = 'c';
aio->aio_fildes = sd;
aio->aio_buf = buf;
aio->aio_nbytes = 3;
aio->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
aio->aio_sigevent.sigev_signo = SIGIO;
aio->aio_sigevent.sigev_value.sival_ptr = &aio;
struct sigaction sig_act;
sigemptyset(&sig_act.sa_mask);
sig_act.sa_flags = SA_SIGINFO;
sig_act.sa_sigaction = aio_completion_handler;
sigaction(SIGIO, &sig_act, NULL);
errno = 0;
int ret = aio_write(aio);
if (ret == -1) {
perror(NULL);
}
assert(ret != -1);
}
UPDATE (Feb 2010): OSX does not support AIO on sockets at all. Bummer!
The presented code was tested on Mountain Lion 10.8.2. It works with a small correction.
The line
"aio->aio_fildes = sd;"
should be changed for example to:
aio->aio_fildes = open( "/dev/null", O_RDWR);
to get the expected result.
see manual. "The aio_write() function allows the calling process to perform an asynchronous write to a previously opened file."
I have code very similar to yours on 10.6.2 (but writing to a file) working without any problems - so it is possible to do what you're trying.
Just out of curiosity, what value are you using for the SIGIO constant ?
I found that an invalid value here in OS X would casue aio_write to fail - so
I always pass SIGUSR1.
Maybe check the return value of sigaction() to verify the signal details?
The points raised in your links all point to a different method for raising io completion notifications (e.g. kqueue which is a BSD specific mechanism), but doesn't really answer your question re POSIX methods for async io. and whether they work on Darwin.
The UNIX world really is a mish mash of solutions for this, and it would be really good if there was one tried and tested solutiom that worked across all platforms, alas currently there's not - POSIX being the one that aims for the most consistency.
It's a bit of a stab in the dark, but it might be useful as well to set nonblocking on your socket handle ( i.e. set socket option O_NONBLOCK ) as well as using SIGUSR1
If I get some time I'll work with your socket sample and see if I can get anything out of that too.
Best of luck.
OSX Allows you to use sockets via the (CF)RunLoop. Or getting callbacks from the runloop.
That is the most elegant way I have found to use async IO on mac.
You can use your existing socket and do a CFSocketCreateWithNative. And register callbacks on your runloop.
Here is a small snippet of code that shows how it can be setup, incomplete since I have cut down on a source file...
// This will setup a readCallback
void SocketClass::setupCFCallback() {
CFSocketContext context = { 0, this, NULL, NULL, NULL };
if (CFSocketRef macMulticastSocketRef = CFSocketCreateWithNative(NULL, socketHandle_, kCFSocketReadCallBack,readCallBack, &context)) {
if (CFRunLoopSourceRef macRunLoopSrc = CFSocketCreateRunLoopSource(NULL, macMulticastSocketRef, 0)) {
if (!CFRunLoopContainsSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode)) {
CFRunLoopAddSource(CFRunLoopGetCurrent(), macRunLoopSrc, kCFRunLoopDefaultMode);
macRunLoopSrc_ = macRunLoopSrc;
}
else
CFRelease(macRunLoopSrc);
}
else
CFSocketInvalidate(macMulticastSocketRef);
CFRelease(macMulticastSocketRef);
}
}
void SocketClass::readCallBack(CFSocketRef inref, CFSocketCallBackType type,CFDataRef , const void *, void *info) {
if (SocketClass* socket_ptr = reinterpret_cast<SocketClass*>(info))
socket_ptr->receive(); // do stuff with your socket
}