How to open named pipe when process is started using uri scheme - c

I am developing a uri scheme registering library in C. I need to redirect the second call of the application to the first one so that only one is opened at a time. I decided to use windows named pipes for that. When I start the second instance like start testuri://example it works fine and the first instance receives the value testuri://example. However, when I start it like explorer testuri://example it fails to open the pipe to write.
This is my code for pipes (it is a wrapper for the winapi since I want the code to be cross platform and a Linux version also exists)
#include <windows.h>
#define READONLY GENERIC_READ
#define WRITEONLY GENERIC_WRITE
#define READWRITE GENERIC_WRITE | GENERIC_READ
#define CREATE_EXCLUSIVE CREATE_NEW
#define CREATE CREATE_ALWAYS
typedef struct file_desc {
char* name;
HANDLE hPipe;
int isReadPipe;
} file_desc;
void pipe_create(file_desc* pipe, const char* name) {
pipe->name = (char*) malloc(strlen(name) + 10);
memcpy(pipe->name, "\\\\.\\pipe\\", 10);
strcat(pipe->name, name);
pipe->hPipe = CreateNamedPipe(
TEXT(pipe->name),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
NULL
);
}
int pipe_open(file_desc* pipe, int mode) {
if (mode == READONLY) {
pipe->isReadPipe = TRUE;
return ConnectNamedPipe(pipe->hPipe, NULL);
} return file_open(pipe, pipe->name, mode, 1);
}
void pipe_close(file_desc* pipe) {
if (pipe->isReadPipe) {
DisconnectNamedPipe(pipe->hPipe);
} else file_close(pipe);
}
int file_open(file_desc* pipe, const char* name, int mode, int lock) {
if (!lock) pipe->hPipe = CreateFile(TEXT(name), mode, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL);
else pipe->hPipe = CreateFile(TEXT(name), mode, 0, NULL, OPEN_ALWAYS, 0, NULL);
return pipe->hPipe != INVALID_HANDLE_VALUE;
}
char* file_read(file_desc* pipe, char buf[], unsigned int size) {
DWORD dwRead;
ReadFile(pipe->hPipe, buf, size, &dwRead, NULL);
}
void file_write(file_desc* pipe, const char* str) {
DWORD dwWritten;
WriteFile(pipe->hPipe,str, strlen(str) + 1, &dwWritten, NULL);
}
void file_close(file_desc* pipe) {
CloseHandle(pipe->hPipe);
}
The way I create the pipes is something like this
file_desc* pipe;
pipe_create(pipe, "mypipe");
if (firstInstance) {
pipe_open(pipe, READONLY);
char buf[200];
file_read(pipe, buf, 200);
pipe_close(pipe);
} else {
pipe_open(pipe, WRITEONLY);
file_write(pipe, "test");
pipe_close(pipe);
}
Full code is at https://github.com/germaniuss/libschemehandler just create an obj folder and execute make in MinGW. Then run myapp.exe

I found a solution!
I tried looking at the error code at it returned error 5, access is denied. I found the problem. When opening the first instance I did that with elevated privileges, however, when opening an app using explorer testuri://example a new terminal window without elevated privileges pops up. This causes a problem since the pipe was created as administrator and is being accessed without.
I looked for info on this topic and found this forum exchange https://forums.codeguru.com/showthread.php?548311-RESOLVED-Writing-to-a-named-pipe-coming-from-a-service-(session-0)-without-admin-rights which was exactly what I needed. I just needed to create an all access security descriptor for the named pipe.
I feel like the fact that the pipe is created by default as with admin only access should be more clearly stated, since it says full access is granted by default
A pointer to a SECURITY_ATTRIBUTES structure that specifies a security
descriptor for the new named pipe and determines whether child
processes can inherit the returned handle. If lpSecurityAttributes is
NULL, the named pipe gets a default security descriptor and the handle
cannot be inherited. The ACLs in the default security descriptor for a
named pipe grant full control to the LocalSystem account,
administrators, and the creator owner. They also grant read access to
members of the Everyone group and the anonymous account.
Source MSDN
In the end my pipe_open functions ended up like so
void pipe_create(file_desc* pipe, const char* name) {
// all access secutrity descriptor
PSECURITY_DESCRIPTOR psd = NULL;
BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
psd = (PSECURITY_DESCRIPTOR)sd;
InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(psd, TRUE, (PACL)NULL, FALSE);
SECURITY_ATTRIBUTES sa = {sizeof(sa), psd, FALSE};
pipe->name = (char*) malloc(strlen(name) + 10);
memcpy(pipe->name, "\\\\.\\pipe\\", 10);
strcat(pipe->name, name);
pipe->hPipe = CreateNamedPipe(
TEXT(pipe->name),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
&sa
);
}

Related

How to modify file attributes without race conditions?

I want to modify a single attribute on a file (e.g the read-only attribute). In order to do that, it looks like I have to query the current file attributes with either GetFileAttributes or GetFileInformationByHandle, then set the new attributes with either SetFileAttributes or SetFileInformationByHandle: https://learn.microsoft.com/en-us/windows/win32/fileio/retrieving-and-changing-file-attributes
However that is inherently racy, as the file attributes may change between the query and the update. Is there a method to update file attributes atomically? I would expect there to be an API like ModifyFileAttributes(DWORD addAttributes, DWORD rmAttributes) which would do its best to work atomically. Transactional NTFS is not an option for me because a) it's deprecated b) only works on NTFS.
Thanks!
As mentioned in the comment, FILE_SHARE_READ is a trade-off. The following code is adapted from SetFileInformationByHandle function. SetFileInformationByHandle for hFile2 is ERROR_ACCESS_DENIED.
#include <Windows.h>
#include <Tchar.h>
int main()
{
//...
HANDLE hFile1 = CreateFile(TEXT("tempfile"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
0,
NULL);
HANDLE hFile2 = CreateFile(TEXT("tempfile"),
GENERIC_READ,
FILE_SHARE_READ| FILE_SHARE_WRITE,
NULL,
OPEN_ALWAYS,
0,
NULL);
if (hFile1 != INVALID_HANDLE_VALUE && hFile2 != INVALID_HANDLE_VALUE)
{
HANDLE hFile = hFile1;
//HANDLE hFile = hFile2;
FILE_BASIC_INFO fdi{};
fdi.FileAttributes = FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_NORMAL;
BOOL fResult = SetFileInformationByHandle(hFile,
FileBasicInfo,
&fdi,
sizeof(FILE_BASIC_INFO));
if (fResult)
{
// File will be deleted upon CloseHandle.
_tprintf(TEXT("SetFileInformationByHandle marked tempfile for deletion\n"));
// ...
// Now use the file for whatever temp data storage you need,
// it will automatically be deleted upon CloseHandle or
// application termination.
// ...
}
else
{
_tprintf(TEXT("error %lu: SetFileInformationByHandle could not mark tempfile for deletion\n"),
GetLastError());
}
CloseHandle(hFile);
// At this point, the file is closed and deleted by the system.
}
else
{
_tprintf(TEXT("error %lu: could not create tempfile\n"),
GetLastError());
}
//...
}

Read chardevice with libevent

I wrote a chardevice that passes some messages received from the network to an user space application. The user space application has to both read the chardevice and send/receive messages via TCP sockets to other user-space applications. Both read and receiving should be blocking.
Since Libevent is able to handle multiple events at the same time, I thought registering an event for the file created by the chardevice and an event for a socket would just work, but I was wrong.
But a chardevice creates a "character special file", and libevent seems to not be able to block. If I implement a blocking mechanism inside the chardevice, i.e. mutex or semaphore, then the socket event blocks too, and the application cannot receive messages.
The user space application has to accept outside connections at any time.
Do you know how to make it work? Maybe also using another library, I just want a blocking behaviour for both socket and file reader.
Thank you in advance.
Update: Thanks to #Ahmed Masud for the help. This is what I've done
Kernel module chardevice:
Implement a poll function that waits until new data is available
struct file_operations fops = {
...
.read = kdev_read,
.poll = kdev_poll,
};
I have a global variable to handle if the user space has to stop, and a wait queue:
static working = 1;
static wait_queue_head_t access_wait;
This is the read function, I return -1 if there is an error in copy_to_user, > 0 if everything went well, and 0 if the module has to stop. used_buff is atomic since it handles the size of a buffer shared read by user application and written by kernel module.
ssize_t
kdev_read(struct file* filep, char* buffer, size_t len, loff_t* offset)
{
int error_count;
if (signal_pending(current) || !working) { // user called sigint
return 0;
}
atomic_dec(&used_buf);
size_t llen = sizeof(struct user_msg) + msg_buf[first_buf]->size;
error_count = copy_to_user(buffer, (char*)msg_buf[first_buf], llen);
if (error_count != 0) {
atomic_inc(&used_buf);
paxerr("send fewer characters to the user");
return error_count;
} else
first_buf = (first_buf + 1) % BUFFER_SIZE;
return llen;
}
When there is data to read, I simply increment used_buf and call wake_up_interruptible(&access_wait).
This is the poll function, I just wait until the used_buff is > 0
unsigned int
kdev_poll(struct file* file, poll_table* wait)
{
poll_wait(file, &access_wait, wait);
if (atomic_read(&used_buf) > 0)
return POLLIN | POLLRDNORM;
return 0;
}
Now, the problem here is that if I unload the module while the user space application is waiting, the latter will go into a blocked state and it won't be possible to stop it. That's why I wake up the application when the module is unloaded
void
kdevchar_exit(void)
{
working = 0;
atomic_inc(&used_buf); // increase buffer size to application is unlocked
wake_up_interruptible(&access_wait); // wake up application, but this time read will return 0 since working = 0;
... // unregister everything
}
User space application
Libevent by default uses polling, so simply create an event_base and a reader event.
base = event_base_new();
filep = open(fname, O_RDWR | O_NONBLOCK, 0);
evread = event_new(base, filep, EV_READ | EV_PERSIST,
on_read_file, base);
where on_read_file simply reads the file, no poll call is made (libevent handles that):
static void
on_read_file(evutil_socket_t fd, short event, void* arg)
{
struct event_base* base = arg;
int len = read(...);
if (len < 0)
return;
if (len == 0) {
printf("Stopped by kernel module\n");
event_base_loopbreak(base);
return;
}
... // handle message
}

Win32 API named pipe, All pipe instances are busy

I'm trying to write in a named pipe and read back the same thing. Consider the following code snippet (the error handling is stripped for brevity):
const char * pipeName = "\\\\.\\pipe\\pipe";
const char * buffWrite = "SOME TEXT";
unsigned buffLength = strlen(buffWrite);
char buffRead[1024];
DWORD nWritten, nRead;
HANDLE hPipe = CreateNamedPipe(pipeName,
PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, 0);
HANDLE hFile = CreateFile(pipeName, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
WriteFile(hFile, buffWrite, buffLength, &nWritten, 0);
CloseHandle(hFile);
//the next line fails with >>All pipe instances are busy.<<
hFile = CreateFile(pipeName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
ReadFile(hFile, buffRead, buffLength, &nRead, 0);
...
However when I try reopen the pipe for reading the CreateFile call fails with "All pipes are busy."
What am I missing here?
EDIT.
Peeking works fine, i.e.
DWORD nRead, nTotal, nLeft;
PeekNamedPipe(hPipe, buffRead, buffLength, &nRead, &nTotal, &nLeft);
returns the written data correctly.
REMARK.
This is a proof of concept for something larger. No new threads and processes will be involved.
The reason you're getting that specific error code is that you only created one instance of the named pipe, and you've already used it. (You can create a new instance by calling CreateNamedPipe a second time, or you can reuse an existing instance by calling DisconnectNamedPipe.)
However, based on your commentary, I believe you want the call to ReadFile to retrieve the data written by the call to WriteFile, i.e., you want the same instance of the pipe, not a new one.
To do that, do not open a new handle. Use the existing handle, hPipe.
(Note that each pipe instance has two ends; a server end and a client end. The handle from CreateNamedPipe is always to the server end, and the handle from CreateFile is always to the client end. Data written to the server end can only be read from the client end, and vice-versa.)
You are trying to use named pipe as some kind of buffer - client connects to it, puts some data, then disconnects, after that other client connects and retrieves this data. This is invalid approach, named pipe is just that - a pipe, it has two sides - server side and client side, server and client could communicate through it. Usual pipe usage scenario:
Server creates named pipe using CreateNamedPipe function;
Server begin waiting for the client connections using ConnectNamedPipe method;
Client creates its side of the pipe using CreateFile API call;
Server and client communicate using ReadFile/WriteFile;
Pipe is closed with DisconnectNamedPipe and could be reactivated again with ConnectNamedPipe.
You could see complete example in the MSDN here.
It is because both extremities of the pipe have allready be opened (*)...
First was opened with the CreatePipe call, the other was with the first CreateFile call. You should not try to open one time more the pipe, but simply read from the hPipe HANDLE :
const char * pipeName = "\\\\.\\pipe\\pipe";
const char * buffWrite = "SOME TEXT";
unsigned buffLength = strlen(buffWrite);
char buffRead[1024];
DWORD nWritten, nRead;
HANDLE hPipe = CreateNamedPipe(pipeName,
PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, 0);
HANDLE hFile = CreateFile(pipeName, GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
WriteFile(hFile, buffWrite, buffLength, &nWritten, 0);
CloseHandle(hFile);
ReadFile(hPipe, buffRead, buffLength, &nRead, 0); // nRead=9, buffRead="SOME TEXT"
...
(*) You did specify PIPE_UNLIMITED_INSTANCES for the nMaxInstances parameter in CreateNamedPipe call, but as you never called ConnectNamedPipe to create other endpoints, only one CreateFile was allowed.

Best solution for dynamic account connection in C?

I'm not very familiar with C design patterns and searching for the best solution for the following problem. I want to write a little chat client based on libpurple.
While running the program I want to be able to connect and disconnect several instant message accounts. The connect and disconnect calls should be passed over command line, but waiting for input with gets(); is no solution, because the program should run all the time getting new messages from the already connected instant message accounts.
You probably want to use poll (or select) for handling the events. So after establishing the connections, you have the file descriptors, and in addition you have the standard input, which also has a file descriptor from the OS (namely 0), and you can pass all those file descriptors to poll, which notifies you when there is incoming data on any of the file descriptors. Example code:
/* fd1, fd2 are sockets */
while(1) {
pollfd fds[3];
int ret;
fds[0].fd = fd1;
fds[1].fd = fd2;
fds[2].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].events = POLLIN;
fds[2].events = POLLIN;
ret = poll(fds, 3, -1); /* poll() blocks, but you can set a timeout here */
if(ret < 0) {
perror("poll");
}
else if(ret == 0) {
printf("timeout\n");
}
else {
if(fds[0].revents & POLLIN) {
/* incoming data from fd1 */
}
if(fds[0].revents & (POLLERR | POLLNVAL)) {
/* error on fd1 */
}
if(fds[1].revents & POLLIN) {
/* incoming data from fd2 */
}
if(fds[1].revents & (POLLERR | POLLNVAL)) {
/* error on fd2 */
}
if(fds[2].revents & POLLIN) {
/* incoming data from stdin */
char buf[1024];
int bytes_read = read(STDIN_FILENO, buf, 1024);
/* handle input, which is stored in buf */
}
}
}
You didn't mention the OS. This works for POSIX (OS X, Linux, Windows with mingw). If you need to use the Win32 API, it'll look a bit different but the principle is the same.
Check out select(2). I'm not really sure how libpurple works, but if it allows notification via file-descriptor (like a file or socket), then select is your solution.
You could also try creating a seperate thread with pthread_create(3). That way it can block on gets (or whatever) while the rest of your program does it's thing.

Monitoring directory using ReadDirectoryChangesW API

I am trying to monitor a directory e:\test using ReadDirectoryChangesW API.
My Code :
#define UNICODE
#define WIN32_WINNT 0x0500
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
HANDLE hDir;
int _tmain(int argc, _TCHAR* argv[])
{
FILE_NOTIFY_INFORMATION fniDir;
DWORD i = 0;
hDir = CreateFile(_T("e:\\test"), GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
ReadDirectoryChangesW(hDir, &fniDir, sizeof(fniDir), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME, &i, NULL, NULL);
while(TRUE)
{
if(i>0)
wprintf(L"%s", fniDir.FileName);
}
CloseHandle(hDir);
return 0;
}
I don't know what's wrong with my code as I haven't understood ReadDirectoryChangesW documentation completely, specially the LPOVERLAPPED parameters.
When I run the code I don't get any output, except for a blank console window. Can someone point me in a right direction?
Thanks.
You only need the overlapped struct if you plan on catching the changes notifications asynchronously. In your code you don't need it.
Here's how you do it.
HANDLE hDir = CreateFile(
p.string().c_str(), /* pointer to the file name */
FILE_LIST_DIRECTORY, /* (this is important to be FILE_LIST_DIRECTORY!) access (read-write) mode */
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* (file share write is needed, or else user is not able to rename file while you hold it) share mode */
NULL, /* security descriptor */
OPEN_EXISTING, /* how to create */
FILE_FLAG_BACKUP_SEMANTICS, /* file attributes */
NULL /* file with attributes to copy */
);
if(hDir == INVALID_HANDLE_VALUE){
throw runtime_error(string("Could not open ").append(p.string()).append(" for watching!"));
}
FILE_NOTIFY_INFORMATION buffer[1024];
DWORD BytesReturned;
while( ReadDirectoryChangesW(
hDir, /* handle to directory */
&buffer, /* read results buffer */
sizeof(buffer), /* length of buffer */
TRUE, /* monitoring option */
FILE_NOTIFY_CHANGE_LAST_WRITE, /* filter conditions */
&BytesReturned, /* bytes returned */
NULL, /* overlapped buffer */
NULL)){
do{
//CANT DO THIS! FileName is NOT \0 terminated
//wprintf("file: %s\n",buffer.FileName);
buffer += buffer.NextEntryOffset;
}while(buffer.NextEntryOffset);
}

Resources