llseek not behaving in kernel driver - linux-device-driver

I have been writing a lcd kernel driver for a LCD module. All was going well, I can write to the display, create a /dev/lcd node that I can write into and it will display the results on the screen. I thought using the llseek fops callback to position the cursor on the lcd would be good, this way I could use rewind fseek etc. However it is not working as I expected, below is a summary of what I am seeing:
The relevant lines of code from the driver side are:
loff_t lcd_llseek(struct file *filp, loff_t off, int whence)
{
switch (whence) {
case 0: // SEEK_SET
if (off > 4*LINE_LENGTH || off < 0) {
printk(KERN_ERR "unsupported SEEK_SET offset %llx\n", off);
return -EINVAL;
}
lcd_gotoxy(&lcd, off, 0, WHENCE_ABS);
break;
case 1: // SEEK_CUR
if (off > 4*LINE_LENGTH || off < -4*LINE_LENGTH) {
printk(KERN_ERR "unsupported SEEK_CUR offset %llx\n", off);
return -EINVAL;
}
lcd_gotoxy(&lcd, off, 0, WHENCE_REL);
break;
case 2: // SEEK_END (not supported, hence fall though)
default:
// how did we get here !
printk(KERN_ERR "unsupported seek operation\n");
return -EINVAL;
}
filp->f_pos = lcd.pos;
printk(KERN_INFO "lcd_llseek complete\n");
return lcd.pos;
}
int lcd_open(struct inode *inode, struct file *filp)
{
if (!atomic_dec_and_test(&lcd_available)) {
atomic_inc(&lcd_available);
return -EBUSY; // already open
}
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.write = lcd_write,
.llseek = lcd_llseek,
.open = lcd_open,
.release = lcd_release,
};
int lcd_init(void)
{
...
// allocate a new dev number (this can be dynamic or
// static if passed in as a module param)
if (major) {
devno = MKDEV(major, 0);
ret = register_chrdev_region(devno, 1, MODULE_NAME);
} else {
ret = alloc_chrdev_region(&devno, 0, 1, MODULE_NAME);
major = MAJOR(devno);
}
if (ret < 0) {
printk(KERN_ERR "alloc_chrdev_region failed\n");
goto fail;
}
// create a dummy class for the lcd
cl = class_create(THIS_MODULE, "lcd");
if (IS_ERR(cl)) {
printk(KERN_ERR "class_simple_create for class lcd failed\n");
goto fail1;
}
// create cdev interface
cdev_init(&cdev, &fops);
cdev.owner = THIS_MODULE;
ret = cdev_add(&cdev, devno, 1);
if (ret) {
printk(KERN_ERR "cdev_add failed\n");
goto fail2;
}
// create /sys/lcd/fplcd/dev so udev will add our device to /dev/fplcd
device = device_create(cl, NULL, devno, NULL, "lcd");
if (IS_ERR(device)) {
printk(KERN_ERR "device_create for fplcd failed\n");
goto fail3;
}
...
}
To test the lseek call I have the following unit test:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define log(msg, ...) fprintf(stdout, __FILE__ ":%s():[%d]:" msg, __func__, __LINE__, __VA_ARGS__)
int lcd;
void test(void)
{
int k;
// a lot of hello's
log("hello world test\n",1);
if (lseek(lcd, 0, SEEK_CUR) == -1) {
log("failed to seek\n", 1);
}
}
int main(int argc, char **argv)
{
lcd = open("/dev/lcd", O_WRONLY);
if (lcd == -1) {
perror("unable to open lcd");
exit(EXIT_FAILURE);
}
test();
close(lcd);
return 0;
}
The files are cross compiled like so:
~/Workspace/ts4x00/lcd-module$ cat Makefile
obj-m += fls_lcd.o
all:
make -C $(KPATH) M=$(PWD) modules
$(CROSS_COMPILE)gcc -g -fPIC $(CFLAGS) lcd_unit_test.c -o lcd_unit_test
clean:
make -C $(KPATH) M=$(PWD) clean
rm -rf lcd_unit_test
~/Workspace/ts4x00/lcd-module$ make CFLAGS+="-march=armv4 -ffunction-sections -fdata-sections"
make -C ~/Workspace/ts4x00/linux-2.6.29 M=~/Workspace/ts4x00/lcd-module modules
make[1]: Entering directory `~/Workspace/ts4x00/linux-2.6.29'
CC [M] ~/Workspace/ts4x00/lcd-module/fls_lcd.o
~/Workspace/ts4x00/lcd-module/fls_lcd.c:443: warning: 'lcd_entry_mode' defined but not used
Building modules, stage 2.
MODPOST 1 modules
CC ~/Workspace/ts4x00/lcd-module/fls_lcd.mod.o
LD [M] ~/Workspace/ts4x00/lcd-module/fls_lcd.ko
make[1]: Leaving directory `~/Workspace/ts4x00/linux-2.6.29'
~/Workspace/ts4x00/arm-2008q3/bin/arm-none-linux-gnueabi-gcc -g -fPIC -march=armv4 -ffunction-sections -fdata-sections lcd_unit_test.c -o lcd_unit_test
This is the output of running the driver with the unit test is:
root#ts4700:~/devel# insmod ./fls_lcd.ko
root#ts4700:~/devel# ./lcd_unit_test
lcd_unit_test.c:test():[61]:hello world test
lcd_unit_test.c:test():[63]:failed to seek
root#ts4700:~/devel# dmesg
FLS LCD driver started
unsupported SEEK_SET offset bf0a573c
I cannot figure out why the parameters are being mucked up so badly on the kernel side, I tried to SEEK_CUR to position 0 and in the driver I get a SEEK_SET (no matter what I put in the unit test) and a crazy big number for off?
Does anyone know what is going on please ?
btw I am compiling for kernel 2.6.29 on a arm dev kit

OK sorry guys after trying to debug this all last night it comes down to compiling against the wrong kernel (I had KPATH left to a different config of the kernel than was on the sdcard)
sorry for wasting everyones time, but hopefully if someone is seeing what looks like a crazy stack in their kernel driver this might set them straight.
oh and thanks for all the help :)

Related

How to create a snort content rule

I am new into using snort and I don't know how to properly create rules.
I want someone to explain me how to create a rule for detection of a specific content. For example: I want to generate an alert when I search on Google the word 'terrorism'.
I tried to create the rule with what I've seen on Youtube or Google, as examples, but none of them works and I don't know what to try anymore. For instance, I am using Snort 2.9.9
alert tcp $HOME_NET any -> $EXTERNAL_NET any (msg:"terrorism content found"; content:"terrorism"; nocase; sid:1000000;)
I don't have any errors from the local.rules file, but I got the line 'include $RULE_PATH/snort.rules' commented because of some problems with PulledPork.
I expect to have an alert in the CLI, but there is no output.
I know that this is already too late but here's the answer for future reference.
The packets are probably being sent using HTTPS connection (which is why they are encrypted).
This might be a reason why there are no alerts.
Please refer here for a detailed explanation.
rules are ready, u just replace, alert with sdrop:
find /home/www \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'
and you can use include in config file
O.K
Answer is here: http://manpages.ubuntu.com/manpages/xenial/man8/u2spewfoo.8.html
Download Snort source, Make logs costume, write ur code to get log stream in control
Then Build source and run
Be successful :)
It is possible to send alert messages and some packet relevant data
from snort through a unix socket, to perform additional separate
processing of alert data.
Snort has to be built with spo_unsock.c/h output plugin is built in and
-A unsock (or its equivalent through the config file) is
used. The unix socket file should be created in /dev/snort_alert. Your
‘client’ code should act as ‘server’ listening to this unix socket.
Snort will be sending you Alertpkt structures which contain alert
message, event id. Original datagram, libpcap pkthdr, and offsets to
datalink, netlayer, and transport layer headers.
Below is an example how unix sockets could be used. If you have any
comments bug reports, and feature requests, please contact
snort-devel#lists.sourceforge.net or drop me an email to fygrave at
tigerteam dot net.
-Fyodor
[for copyright notice, see snort distribution code]
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include
#include "snort.h"
int sockfd;
void
sig_term (int sig)
{
printf (“Exiting!\n”);
close (sockfd);
unlink (UNSOCK_FILE);
exit (1);
}
int
main (void)
{
struct sockaddr_un snortaddr;
struct sockaddr_un bogus;
Alertpkt alert;
Packet *p;
int recv;
socklen_t len = sizeof (struct sockaddr_un);
if ((sockfd = socket (AF_UNIX, SOCK_DGRAM, 0)) < 0)
{
perror (“socket”);
exit (1);
}
bzero (&snortaddr, sizeof (snortaddr));
snortaddr.sun_family = AF_UNIX;
strcpy (snortaddr.sun_path, UNSOCK_FILE);
if (bind (sockfd, (struct sockaddr *) &snortaddr, sizeof (snortaddr)) < 0)
{
perror (“bind”);
exit (1);
}
signal(SIGINT, sig_term);
while ((recv = recvfrom (sockfd, (void *) &alert, sizeof (alert),
0, (struct sockaddr *) &bogus, &len)) > 0)
{
/* do validation of recv if you care */
if (!(alert.val & NOPACKET_STRUCT))
{
if ((p = calloc (1, sizeof (Packet))) == NULL)
{
perror ("calloc");
exit (1);
}
p->pkt = alert.pkt;
p->pkth = &alert.pkth;
if (alert.dlthdr)
p->eh = (EtherHdr *) (alert.pkt + alert.dlthdr);
if (alert.nethdr)
{
p->iph = (IPHdr *) (alert.pkt + alert.nethdr);
if (alert.transhdr)
{
switch (p->iph->ip_proto)
{
case IPPROTO_TCP:
p->tcph = (TCPHdr *) (alert.pkt + alert.transhdr);
break;
case IPPROTO_UDP:
p->udph = (UDPHdr *) (alert.pkt + alert.transhdr);
break;
case IPPROTO_ICMP:
p->icmph = (ICMPHdr *) (alert.pkt + alert.transhdr);
break;
default:
printf ("My, that's interesting.\n");
} /* case */
} /* thanshdr */
} /* nethdr */
if (alert.data)
p->data = alert.pkt + alert.data;
/* now do whatever you want with these packet structures */
} /* if (!NOPACKET_STRUCT) */
printf ("%s [%d]\n", alert.alertmsg, alert.event.event_id);
if (!(alert.val & NOPACKET_STRUCT))
if (p->iph && (p->tcph || p->udph || p->icmph))
{
switch (p->iph->ip_proto)
{
case IPPROTO_TCP:
printf ("TCP from: %s:%d ",
inet_ntoa (p->iph->ip_src),
ntohs (p->tcph->th_sport));
printf ("to: %s:%d\n", inet_ntoa (p->iph->ip_dst),
ntohs (p->tcph->th_dport));
break;
case IPPROTO_UDP:
printf ("UDP from: %s:%d ",
inet_ntoa (p->iph->ip_src),
ntohs (p->udph->uh_sport));
printf ("to: %s:%d\n", inet_ntoa (p->iph->ip_dst),
ntohs (p->udph->uh_dport));
break;
case IPPROTO_ICMP:
printf ("ICMP type: %d code: %d from: %s ",
p->icmph->type,
p->icmph->code, inet_ntoa (p->iph->ip_src));
printf ("to: %s\n", inet_ntoa (p->iph->ip_dst));
break;
}
}
}
perror (“recvfrom”);
close (sockfd);
unlink (UNSOCK_FILE);
return 0;
}

Most UEFI protocols are reported as "unsupported"

I'm writing an EFI executable on a SoC device (Up Board) to help us automate BIOS updates and PXE boots for installating our software on numerous devices.
The problem I have is that seemingly most of the protocols in the specification are "unsupported" on this platform, even basic file system tasks. The only one I've successfully used is the LOADED_IMAGE_PROTOCOL. I'm using gnu-efi to compile the code, basing my code to this example https://mjg59.dreamwidth.org/18773.html. Is there something I'm doing wrong, or is this simply the firmware absolutely not implementing any of the protocols?
The American Megatrends utility "AfuEfix64.efi" is capable of retrieving BIOS information, and the SoC BIOS updates are done using an Intel's EFI executable. Both are closed source, unfortunately. My initial idea would have been to write a script that parses the output from these executables, but I don't think the EFI shell has much features for this type of tasks, so I moved on to writing an EFI executable to perform this.
Minimal code showing this:
#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>
// gnu-efi does not currently define firmware management
// https://raw.githubusercontent.com/tianocore/edk2/master/MdePkg/Include/Protocol/FirmwareManagement.h
#include "efifirmware.h"
// gnu-efi does not currently define this
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
{ 0x4006c0c1, 0xfcb3, 0x403e, \
{ 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d }}
typedef EFI_LOAD_FILE_PROTOCOL EFI_LOAD_FILE2_PROTOCOL;
void tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
EFI_STATUS status;
status = uefi_call_wrapper(systemTable->BootServices->HandleProtocol, 3,
imageHandle, &proto_guid, out);
if (EFI_ERROR(status)) {
Print(L"HandleProtocol error for %s: %r\n", name, status);
} else {
Print(L"Protocol %s is supported\n", name);
}
}
void tryProtocols(EFI_HANDLE imgh, EFI_SYSTEM_TABLE* syst) {
EFI_LOADED_IMAGE* loaded_image = NULL;
EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;
tryProtocol(guid_imgprot, (void**)&loaded_image,
L"LOADED_IMAGE_PROTOCOL", imgh, syst);
Print(L"Image base: %lx\n", loaded_image->ImageBase);
Print(L"Image size: %lx\n", loaded_image->ImageSize);
Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));
EFI_FIRMWARE_MANAGEMENT_PROTOCOL* fw_manage = NULL;
EFI_GUID guid_fwman_prot = EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
tryProtocol(guid_fwman_prot, (void**)&fw_manage,
L"FIRMWARE_MANAGEMENT_PROTOCOL", imgh, syst);
EFI_LOAD_FILE_PROTOCOL* load_file = NULL;
EFI_GUID guid_loadf_prot = EFI_LOAD_FILE_PROTOCOL_GUID;
tryProtocol(guid_loadf_prot, (void**)&load_file,
L"LOAD_FILE_PROTOCOL", imgh, syst);
EFI_LOAD_FILE2_PROTOCOL* load_file2 = NULL;
EFI_GUID guid_loadf2_prot = EFI_LOAD_FILE2_PROTOCOL_GUID;
tryProtocol(guid_loadf2_prot, (void**)&load_file2,
L"LOAD_FILE2_PROTOCOL", imgh, syst);
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
L"SIMPLE_FILE_SYSTEM_PROTOCOL", imgh, syst);
EFI_DISK_IO_PROTOCOL* disk = NULL;
EFI_GUID guid_disk_io_prot = EFI_DISK_IO_PROTOCOL_GUID;
tryProtocol(guid_disk_io_prot, (void**)&disk,
L"DISK_IO_PROTOCOL", imgh, syst);
EFI_BLOCK_IO_PROTOCOL* block = NULL;
EFI_GUID guid_block_io_prot = EFI_BLOCK_IO_PROTOCOL_GUID;
tryProtocol(guid_block_io_prot, (void**)&block,
L"BLOCK_IO_PROTOCOL", imgh, syst);
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
InitializeLib(imageHandle, systemTable);
Print(L"Image loaded\n");
tryProtocols(imageHandle, systemTable);
return EFI_SUCCESS;
}
Here's the output for running it:
EFI Shell version 2.40 [5.11]
Current running mode 1.1.2
Device mapping table
fs0 :HardDisk - Alias hd6b blk0
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
fs1 :Removable HardDisk - Alias hd9g0a0b blk1
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
blk0 :HardDisk - Alias hd6b fs0
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(1,GPT,2DCDDADD-8F3A-4A77-94A9-010A8C700BB8,0x800,0x100000)
blk1 :Removable HardDisk - Alias hd9g0a0b fs1
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)/HD(1,MBR,0x528E6A1F,0x800,0x1CDE800)
blk2 :HardDisk - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)/HD(2,GPT,8AC0F94E-3CA2-4C03-BE00-3A69721CC391,0x100800,0x1C1F7DF)
blk3 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x0)
blk4 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x1)
blk5 :BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x10,0x0)/Ctrl(0x2)
blk6 :Removable BlockDevice - Alias (null)
PciRoot(0x0)/Pci(0x14,0x0)/USB(0x6,0x0)/USB(0x0,0x0)
Press ESC in 4 seconds to skip startup.nsh, any other key to continue.
fs1:\> tryprotocols.efi
Image loaded
Protocol LOADED_IMAGE_PROTOCOL is supported
Image base: 55BA6000
Image size: F000
Image file: \/tryprotocols.efi
HandleProtocol error for FIRMWARE_MANAGEMENT_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE_PROTOCOL: Unsupported
HandleProtocol error for LOAD_FILE2_PROTOCOL: Unsupported
HandleProtocol error for SIMPLE_FILE_SYSTEM_PROTOCOL: Unsupported
HandleProtocol error for DISK_IO_PROTOCOL: Unsupported
HandleProtocol error for BLOCK_IO_PROTOCOL: Unsupported
fs1:\>
And here's the Makefile I use to compile it:
ARCH=x86_64
OBJS=tryprotocols.o
TARGET=tryprotocols.efi
TARGET_SO=$(TARGET:.efi=.so)
GNUEFIDIR=/home/gekko/gnu-efi-3.0.8
EFIINC=$(GNUEFIDIR)/inc
EFIINCS=-I$(EFIINC) -I$(EFIINC)/$(ARCH) -I$(EFIINC)/protocol
LIB=$(GNUEFIDIR)/$(ARCH)/lib
EFILIB=$(GNUEFIDIR)/gnuefi
EFI_CRT_OBJS=$(GNUEFIDIR)/$(ARCH)/gnuefi/crt0-efi-$(ARCH).o
EFI_LDS=$(EFILIB)/elf_$(ARCH)_efi.lds
CFLAGS=$(EFIINCS) -fno-stack-protector -fpic \
-fshort-wchar -mno-red-zone -Wall
ifeq ($(ARCH),x86_64)
CFLAGS += -DEFI_FUNCTION_WRAPPER
endif
LDFLAGS=-nostdlib -znocombreloc -T $(EFI_LDS) -shared \
-Bsymbolic -L $(EFILIB) -L $(LIB) $(EFI_CRT_OBJS)
all: $(TARGET)
$(TARGET_SO): $(OBJS)
ld $(LDFLAGS) $(OBJS) -o $# -lefi -lgnuefi
%.efi: %.so
objcopy -j .text -j .sdata -j .data -j .dynamic \
-j .dynsym -j .rel -j .rela -j .reloc \
--target=efi-app-$(ARCH) $^ $#
clean:
rm -f *.o
rm -f $(TARGET)
rm -f $(TARGET_SO)
EDIT: Modified version that properly uses LocateProtocol() to find the protocols, and uses them to open and close a file.
#include <efi.h>
#include <efilib.h>
#include <x86_64/efibind.h>
BOOLEAN tryProtocol(EFI_GUID proto_guid, void** out, const CHAR16* name,
EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
*out = NULL;
EFI_STATUS status;
EFI_HANDLE interface = NULL;
status = uefi_call_wrapper(systemTable->BootServices->LocateProtocol, 3,
&proto_guid, NULL, &interface);
if (EFI_ERROR(status)) {
Print(L"LocateProtocol error for %s: %r\n", name, status);
return FALSE;
}
Print(L"Locate protocol address: %s, %x\n", name, interface);
*out = interface;
return TRUE;
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE imageHandle, EFI_SYSTEM_TABLE* systemTable) {
InitializeLib(imageHandle, systemTable);
Print(L"Image loaded\n");
EFI_LOADED_IMAGE* loaded_image = NULL;
EFI_GUID guid_imgprot = LOADED_IMAGE_PROTOCOL;
if (tryProtocol(guid_imgprot, (void**)&loaded_image,
L"LOADED_IMAGE_PROTOCOL", imageHandle, systemTable) != TRUE) {
Print(L"Missing required protocol. Aborting\n");
return EFI_SUCCESS;
}
Print(L"Image base: %lx\n", loaded_image->ImageBase);
Print(L"Image size: %lx\n", loaded_image->ImageSize);
Print(L"Image file: %s\n", DevicePathToStr(loaded_image->FilePath));
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL* simple_fs = NULL;
EFI_GUID guid_simple_fs_prot = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
if (tryProtocol(guid_simple_fs_prot, (void**)&simple_fs,
L"EFI_SIMPLE_FILE_SYSTEM_PROTOCOL", imageHandle, systemTable) != TRUE) {
Print(L"Missing required protocol. Aborting\n");
return EFI_SUCCESS;
}
EFI_FILE_PROTOCOL* vol_proto = NULL;
EFI_STATUS status;
Print(L"dereffing\n");
Print(L"address of OpenVolume: %x\n", simple_fs->OpenVolume);
status = uefi_call_wrapper(simple_fs->OpenVolume, 2, simple_fs, &vol_proto);
if (EFI_ERROR(status)) {
Print(L"Error opening volume: %r\n", status);
return EFI_SUCCESS;
}
Print(L"SIMPLE_FILE_SYSTEM volume opened\n");
EFI_FILE_PROTOCOL* f;
CHAR16 fname[10] = L"foo.txt\0";
UINT64 openmode = EFI_FILE_MODE_READ;
UINT64 attr = 0;
status = uefi_call_wrapper(vol_proto->Open, 5, vol_proto, &f, fname, openmode, attr);
if (EFI_ERROR(status)) {
Print(L"Error opening file: %r\n", status);
return EFI_SUCCESS;
}
Print(L"opened file %s\n", fname);
// Spec says can only return EFI_SUCCESS
status = uefi_call_wrapper(vol_proto->Close, 1, f);
Print(L"Closed file\n");
return EFI_SUCCESS;
}
Where and how to access a specific protocol is not statically defined by the UEFI specification, but something that needs to be discovered at runtime. While a bit arduous, it makes it possible to write applications/drivers portable across vastly different UEFI implementations that all conform to the specification.
So you need to LocateProtocol() on each protocol you are intending to use before you can make use of HandleProtocol().
You get away with LOADED_IMAGE_PROTOCOL because that one is initialized with your ImageHandle referring to the instance of the currently executing image (your program).
This is covered in Matthew's post under the section That covers how to use protocols attached to the image handle. How about protocols that are attached to other handles?.

How to get the audio stream from PJSIP when there is no audio hardware device

I want to use PJSIP's C API to record the incoming audio to a file on a machine with no hardware sound device .
I'm unsure about the details, but the sparse documentation for PJSIP suggests it should be
possible through the pjsua_set_null_snd_dev() call.
In the fully functioning (Windows biased) example below the call pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, &param) returns PJMEDIA_AUD_INVALID_DEV in the status.
The code generates this same error on Linux (Ubuntu 14) and Windows 10 when there are no hardware audio devices present.
If there is an hardware audio device driver installed the exact same code works fine on both OSes.
I have compiled the PJSIP libraries with PJMEDIA_AUDIO_DEV_HAS_NULL_AUDIO enabled.
On Linux the presence of the module snd-dummy does not help.
How do I get access to the audio data stream from a SIP call after calling pjsua_set_null_snd_dev()?
#include <pjlib.h>
#include <pjlib-util.h>
#include <pjnath.h>
#include <pjsip.h>
#include <pjsip_ua.h>
#include <pjsip_simple.h>
#include <pjsua-lib/pjsua.h>
#include <pjmedia.h>
#include <pjmedia-codec.h>
#include <pj/log.h>
#include <pj/os.h>
int main(int, char **)
{
// Create pjsua first!
pj_status_t status = pjsua_create();
if (status != PJ_SUCCESS)
{
fprintf(stderr,"pjsua_create error\n");
return -1;
}
// Init pjsua
pjsua_config cfg;
pjsua_logging_config log_cfg;
pjsua_config_default(&cfg);
pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4;
status = pjsua_init(&cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS)
{
fprintf(stderr,"pjsua_init error\n");
return -1;
}
// Proactively list known audio devices so we are sure there are NONE
pjmedia_aud_dev_info info[64];
unsigned info_count = 64;
pjsua_enum_aud_devs(info, &info_count);
fprintf(stderr,"Listing known sound devices, total of [%u]\n", info_count);
for (unsigned i = 0; i<info_count; ++i)
{
fprintf(stderr,"Name [%s]", info[i].name);
}
// Add transport
pjsua_transport_config tcfg;
pjsua_transport_id trans_id;
pjsua_transport_config_default(&tcfg);
tcfg.port = 5060;
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &tcfg, &trans_id);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_transport_create error\n");
return -1;
}
// Initialization is done, now start pjsua
status = pjsua_start();
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_start error\n");
return -1;
}
// Set NULL sound
status = pjsua_set_null_snd_dev();
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_set_null_snd_dev error");
return -1;
}
// Register to a SIP server by creating SIP account, I happen use use Asterisk
pjsua_acc_id acc_id;
fprintf(stderr, "Setting up SIP server registration\n");
{
pjsua_acc_config cfg;
pjsua_acc_config_default(&cfg);
cfg.id = pj_str("sip:6001#10.0.0.21");
cfg.reg_uri = cfg.id; // same as ID
cfg.cred_count = 1;
cfg.cred_info[0].realm = pj_str("*");
cfg.cred_info[0].scheme = pj_str("digest");
cfg.cred_info[0].username = pj_str("6001");
cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
cfg.cred_info[0].data = pj_str("teddy");
status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_acc_add error\n");
return -1;
}
}
fprintf(stderr, "Waiting for SIP server registration to complete....\n");
Sleep(2000); // sleep 2 seconds
// Call extension 9 on my Asterisk server at 10.0.0.21:5060
pj_str_t sip_target(pj_str("sip:9#10.0.0.21"));
fprintf(stderr, "Making call to [%s]\n", sip_target.ptr);
pjsua_call_id call_id;
status = pjsua_call_make_call(acc_id, &sip_target, 0, NULL, NULL, &call_id);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjsua_call_make_call error\n");
return -1;
}
pj_pool_t * pool = nullptr;
pjmedia_port * wav = nullptr;
pjmedia_aud_stream *strm = nullptr;
pool = pj_pool_create(pjmedia_aud_subsys_get_pool_factory(), "wav-audio", 1000, 1000, NULL);
if (nullptr == pool)
{
fprintf(stderr,"Pool creation failed\n");
return -1;
}
// 8kHz, single channel 16bit MS WAV format file
status = pjmedia_wav_writer_port_create(pool, "test.wav", 8000, 1, 320, 16, PJMEDIA_FILE_WRITE_PCM, 0, &wav);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "Error creating WAV file\n");
return -1;
}
pjmedia_aud_param param;
//////////////////////////////////////////////////////
// FAILURE HERE : This is the function call which returns PJMEDIA_AUD_INVALID_DEV
//////////////////////////////////////////////////////
status = pjmedia_aud_dev_default_param(PJMEDIA_AUD_DEFAULT_CAPTURE_DEV, &param);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "pjmedia_aud_dev_default_param()");
return -1;
}
param.dir = PJMEDIA_DIR_CAPTURE;
param.clock_rate = PJMEDIA_PIA_SRATE(&wav->info);
param.samples_per_frame = PJMEDIA_PIA_SPF(&wav->info);
param.channel_count = PJMEDIA_PIA_CCNT(&wav->info);
param.bits_per_sample = PJMEDIA_PIA_BITS(&wav->info);
status = pjmedia_aud_stream_create(&param, &test_rec_cb, &test_play_cb, wav, &strm);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "Error opening the sound stream");
return -1;
}
status = pjmedia_aud_stream_start(strm);
if (status != PJ_SUCCESS)
{
fprintf(stderr, "Error starting the sound device");
return -1;
}
// Spend some time allowing the called party to pick up and recording to proceed
Sleep(10000); // sleep 10 seconds
// Clean up code omitted
return 0;
}
Apologies to the pure of heart for the mix of C and C++ above.
Solved this by loading the Alsa module snd-dummy.
Look in /lib/modules/YOUR_KERNEL_VERSION/modules.dep if its mentioned.
If you have it then load it with modprobe snd-dummy
Otherwise recompile your Kernel to include it as a module or follow the installation in the link above.

Pull GPIO pin low as last act prior to shutting down

I want to signal a power off state by pulling a GPIO pin low for 10 seconds. I have discovered the run levels in the /etc/rc.* directories. Eventually the "halt" command gets run.
I'd like to edit and recompile the halt.c file, but I am unable to locate it. I have two questions:
Is this the right approach?
Where is the source code? (pending positive answer from Q1).
Thank you.
Justin
I haven't used the Raspberry Pi, but on most systems halt, poweroff and reboot are all links to the same binary. Another way to do this would be to just write a program to do what you want and make it the last thing to be called in /etc/rc.d/rc0.d. Runlevel 0 is what the system does when it shuts down.
Here is my take on this problem 'powerctl_pin'
https://pastebin.com/BdzGM5TJ
/*
Raspi3B Power Control Pin Raspbian Jessie
Bilgus -- 2017 CC-BY-SA 3.0
Requires WiringPI http://wiringpi.com/
Compiling:
gcc -o powerctl_pin powerctl_pin.c -l wiringPi && chmod +x powerctl_pin &&
sudo cp powerctl_pin /bin/
Install:
sudo /bin/powerctl_pin 21 install
Uninstall:
sudo /bin/powerctl_pin 21 uninstall
Shutdown:
sudo /bin/powerctl_pin 21 poweroff
/bin/powerctl_pin Broadcom_pin#, mode
Valid Modes: poweroff, poweron, install, uninstall
Works with mosfet latch switch
https://easyeda.com/Bilgus/New_Project-4ce1316bb1f6402985f8d1c4f196448d
Original Circuit:
http://www.mosaic-industries.com/embedded-systems/microcontroller-projects
/raspberry-pi/on-off-power-controller
*/
/*
Raspi3B Power Control Pin Raspbian Jessie
Bilgus -- 2017 CC-BY-SA 3.0
Requires WiringPI http://wiringpi.com/
Compiling:
gcc -o powerctl_pin powerctl_pin.c -l wiringPi && chmod +x powerctl_pin &&
sudo cp powerctl_pin /bin/
Install:
sudo /bin/powerctl_pin 21 install
Uninstall:
sudo /bin/powerctl_pin 21 uninstall
Shutdown:
sudo /bin/powerctl_pin 21 poweroff
/bin/powerctl_pin Broadcom_pin#, mode
Valid Modes: poweroff, poweron, install, uninstall
Works with mosfet latch switch
https://easyeda.com/Bilgus/New_Project-4ce1316bb1f6402985f8d1c4f196448d
Original Circuit:
http://www.mosaic-industries.com/embedded-systems/microcontroller-projects
/raspberry-pi/on-off-power-controller
*/
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <signal.h> //sigterm, sigint()
#include "wiringPi.h"
#define WIRINGPI_CODES 999 /* enable error checking on setup */
#define POWEROFF_SVC "PwrCtl_Pin.service"
#define POWEROFF_SVC_PATH "/lib/systemd/system/"POWEROFF_SVC
#define REQUIRES "shutdown.target umount.target final.target poweroff.target"
#define AFTER "shutdown.target umount.target final.target"
#define BEFORE "systemd-poweroff.service"
#define WANTEDBY "poweroff.target"
#define POWERINT_SVC "PwrCtl_Pin-Interrupt.service"
#define POWERINT_SVC_PATH "/lib/systemd/system/"POWERINT_SVC
static void poweroff_ISR_INIT(int pin);
static int g_pin = 0;
/* enables pullup - unneeded to hold latch */
static void poweron_pin(int pin)
{
pullUpDnControl(pin, PUD_UP);
pinMode(pin, INPUT);
}
/* pulls pin low in short loop to turn power off */
static void poweroff_pin(int pin)
{
pullUpDnControl(pin, PUD_OFF);
int c;
for(c=0;c<20;c++)
{
//pinMode(pin, INPUT);
//pullUpDnControl(pin, PUD_DOWN);
digitalWrite(pin,LOW);
pinMode(pin, OUTPUT);
digitalWrite(pin,LOW);
delay(500);
}
}
/* ISR Called when pin goes from high to low debounce checks that still low */
static void poweroff_ISR(void)
{
delay(100);
if (digitalRead(g_pin) == LOW) /* pin debounce */
{
openlog ("Poweroff ISR", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "ISR Triggered - Shutdown -h now");
closelog();
system("shutdown -h now");
exit(0);
}
else
poweroff_ISR_INIT(g_pin);
}
/* signal handler recieves shutdown signals */
static void signal_handler(int signal_0)
{
if (signal_0 == SIGINT || signal_0 == SIGTERM || signal_0 == SIGSTOP)
exit(0);
}
/* Initialize Poweroff ISR handler */
static void poweroff_ISR_INIT(int pin)
{
g_pin = pin;
pinMode(pin, INPUT);
pullUpDnControl(pin, PUD_UP);
wiringPiISR(pin, INT_EDGE_FALLING, &poweroff_ISR);
while(1)
{
delay(100);
;;
}
}
/* creates service daemons */
static void install(int pin, char *prog_path)
{
FILE *f = fopen(POWEROFF_SVC_PATH, "w");
if (f == NULL)
{
fprintf(stderr, "Error opening file! %s\n", POWEROFF_SVC_PATH);
exit(-6);
}
fprintf(f, "# POWER CONTROL PIN SERVICE \n# %s\n\n", prog_path);
fprintf(f, "[Unit]\nDescription=PowerCtl_Pin pulls GPIO pin %i LOW\n", pin);
fprintf(f, "DefaultDependencies=no\n");
fprintf(f, "Requires=%s\n", REQUIRES);
fprintf(f, "After=%s\nBefore=%s\n\n", AFTER, BEFORE);
fprintf(f, "[Service]\nType=oneshot\n");
fprintf(f, "ExecStart=%s %i poweroff\n\n", prog_path, pin);
fprintf(f, "[Install]\nWantedBy=%s\n\n", WANTEDBY);
fclose(f);
printf("Service file created: %s\n", POWEROFF_SVC_PATH);
system("systemctl enable " POWEROFF_SVC);
FILE *f1 = fopen(POWERINT_SVC_PATH, "w");
if (f1 == NULL)
{
fprintf(stderr, "Error opening file! %s\n", POWERINT_SVC_PATH);
exit(-7);
}
fprintf(f1, "# POWER CONTROL PIN INTERRUPT SERVICE \n");
fprintf(f1, "# %s\n\n", prog_path);
fprintf(f1, "[Unit]\nDescription=PowerCtl_Pin Interrupt watches GPIO ");
fprintf(f1, "for pin %i LOW calls shutdown now\n", pin);
fprintf(f1, "DefaultDependencies=no\n");
fprintf(f1, "After=network.target\n\n");
fprintf(f1, "[Service]\nType=simple\n");
fprintf(f1, "ExecStart=%s %i interrupt &\n", prog_path, pin);
fprintf(f1, "TimeoutSec=0\nRestart=always\n\n");
fprintf(f1, "[Install]\nWantedBy=multi-user.target\n\n");
fclose(f1);
printf("\n\rService file created: %s\n", POWERINT_SVC_PATH);
system("systemctl enable " POWERINT_SVC);
printf("Attempting to start: %s\n\r", POWERINT_SVC_PATH);
system("systemctl start " POWERINT_SVC);
}/*install*/
/* disables/stops service daemons */
static void uninstall(int pin, char *prog_path)
{
system("systemctl disable " POWEROFF_SVC);
printf("Service file still exists: %s\n\n", POWEROFF_SVC_PATH);
system("systemctl stop " POWERINT_SVC);
system("systemctl disable " POWERINT_SVC);
printf("Service file still exists: %s\n", POWERINT_SVC_PATH);
}
int main(int argc, char **argv)
{
int pin = 0;
if (geteuid() != 0)
{
fprintf (stderr, "You need to be root to run this program. (sudo?)\n") ;
exit(-1);
}
putenv("WIRINGPI_CODES=WhOkNoWs??");
if (wiringPiSetupGpio() != 0) /* change to wiringPiSetup() for WiringPi Pins*/
{
fprintf(stderr, "wiringPiSetup error\n");
exit(-2);
}
if(argc < 3)
{
fprintf (stderr, "not enough args poweroff_pin(brcm_pin# mode)\n") ;
fprintf (stderr, "Valid Mode [power[off/on], interrupt, install, uninstall]])\n") ;
exit(-3);
}
else if (sscanf (argv[1],"%i", &pin) != 1)
{
fprintf (stderr, "invalid pin number, poweroff_pin(brcm_pin# mode) ");
fprintf (stderr, "Valid Mode [power[off/on], interrupt, install, uninstall]])\n") ;
exit(-4);
}
else if(strncmp(argv[2],"interrupt",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - interrupt mode pin = %s", argv[0], argv[1]);
closelog();
if (signal(SIGINT, signal_handler) == SIG_ERR)
{
fprintf(stderr, "Unable to register signal handler\n");
exit(-5);
}
poweroff_ISR_INIT(pin);
}
else if(strncmp(argv[2],"poweroff",10) == 0)
{
poweroff_pin(pin);
}
else if(strncmp(argv[2],"install",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - install pin = %s", argv[0], argv[1]);
closelog();
install(pin, argv[0]);
}
else if(strncmp(argv[2],"uninstall",10) == 0)
{
openlog (argv[0], LOG_CONS | LOG_PID | LOG_NDELAY, LOG_USER);
syslog (LOG_NOTICE, "%s - uninstall pin = %s", argv[0], argv[1]);
closelog();
uninstall(pin, argv[0]);
}
else
{
poweron_pin(pin);
}
return 0;
}/*main*/

I/O redirection in a C program

I want to implement a simple "cat file1 > file1" command in a C program. I have tried the following, but it does not work...
main () {
pid_t pid;
FILE *ip, *op;
char *args[3];
printf("Name of the executable program\n\t");
scanf("%s", &name[0]); // I entered cat here
printf("Name of the input file\n\t");
scanf("%s", &name[1]); //file1.txt
printf("Name of the output file\n\t");
scanf("%s", &name[0]); //file2.txt
pid = fork();
if(pid == -1)
perror("fork() error");
else if(pid > 0)
waitpid(-1, NULL, 0);
else if (pid == 0) {
op = fopen(name[2], "w");
close(1);
dup(op);
execlp(name[0], name[1], NULL);
}
return 0;
}// end of main()
I thought the execlp() will run cat file1.txt and its output will be redirected to file2.txt, but it's not and I don't know why. How do I do it?
scanf("%s", &name[0]); // I entered cat here
printf("Name of the input file\n\t");
scanf("%s", &name[1]); //file1.txt
printf("Name of the output file\n\t");
scanf("%s", &name[0]); //file2.txt
Clearly not a C&P of actual code - name should be args, and the last one should be "2" instead of 0.
Also, dup works on file descriptors, not FILE*, so need to look at open rather than fopen, or whatever method gets the fd from a FILE*
The first argument to execlp() is the name to be looked up; the second and following arguments are the argv list, starting with argv[0].
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
For shell I/O redirection, it is easier to open files with open() than to use standard I/O (<stdio.h> and FILE *); you should also close the file you opened after the dup(), though it is easier to use dup2(). You need to allocate space to read the strings into; on many systems, the original code would crash because the pointers in str don't point anywhere. You should normally aim to exit with status 0 only if everything worked; otherwise, exit with a non-zero exit status.
This leads to:
#include <fcntl.h> /* open() */
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h> /* waitpid() */
#include <unistd.h> /* execlp(), fork(), dup2() */
int main(void)
{
pid_t pid;
pid_t corpse;
int status;
char name[3][50];
printf("Name of the executable program\n\t");
if (scanf("%49s", name[0]) != 1)
return(EXIT_FAILURE);
printf("Name of the input file\n\t");
if (scanf("%49s", name[1]) != 1)
return(EXIT_FAILURE);
printf("Name of the output file\n\t");
if (scanf("%49s", name[2]) != 1)
return(EXIT_FAILURE);
pid = fork();
if (pid == -1)
{
perror("fork() error");
return(EXIT_FAILURE);
}
else if (pid > 0)
corpse = waitpid(-1, &status, 0);
else
{
int fd = open(name[2], O_WRONLY|O_CREAT|O_EXCL, 0644);
if (fd < 0)
{
fprintf(stderr, "Failed to open %s for writing\n", name[2]);
return(EXIT_FAILURE);
}
dup2(fd, 1);
close(fd);
execlp(name[0], name[0], name[1], NULL);
fprintf(stderr, "Failed to exec %s\n", name[0]);
return(EXIT_FAILURE);
}
return(corpse == pid && status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
You have to either use fork() a process and reassign it's file descriptors to previously(manually) open()'ed file, or use system() call to make shell handle it for you.