Merge pull request 'Dummy sender and receiver for IPC queue' (#21) from feature/ipc-dummy into main

Reviewed-on: #21
This commit is contained in:
2025-12-06 23:04:55 +01:00
7 changed files with 344 additions and 51 deletions

1
.gitignore vendored
View File

@ -174,3 +174,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder. # option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/ #.idea/
dummy_sender/dummy_sender

View File

@ -37,5 +37,20 @@ systemctl enable --now /lib/systemd/system/reHDDPrinter.service
see https://github.com/pklaus/brother_ql for details for printer access see https://github.com/pklaus/brother_ql for details for printer access
## Test IPC msg queue
### Dummy Sender
```
cd dummy_sender
clear && g++ -Wall main.cpp -o dummy_sender
clear && ./dummy_sender
```
### Dummy Receiver
```
clear && python ./dummy_receiver.py
```
### Clear IPC mgs queue
```
clear && bash ./cleanup_queues.py
```

16
cleanup_queue.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# Cleanup script to remove the IPC message queue
MSG_QUEUE_KEY="0x1b11193c0"
echo "Removing message queue with key: $MSG_QUEUE_KEY"
ipcrm -Q $MSG_QUEUE_KEY 2>/dev/null
if [ $? -eq 0 ]; then
echo "Message queue removed successfully"
else
echo "No message queue found or already removed"
fi
echo "Current message queues:"
ipcs -q

View File

@ -1,53 +1,185 @@
import sysv_ipc #pip install sysv-ipc #!/usr/bin/env python3
import pycstruct #pip install pycstruct # -*- coding: utf-8 -*-
"""Author: Hendrik Schutter, localhorst@mosad.xyz
Date of creation: 2025/12/05
Date of last modification: 2025/12/05
"""
import ctypes
import os
import time
import argparse
# Constants
STR_BUFFER_SIZE = 64
MSG_QUEUE_KEY = 0x1B11193C0
IPC_CREAT = 0o1000
terminate = False
str_buffer_size = 64 class TDriveData(ctypes.Structure):
msg_queue_key = 0x1B11193C0 _fields_ = [
("caDriveIndex", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveHours", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveCycles", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveErrors", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveShredTimestamp", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveShredDuration", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveCapacity", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveState", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveModelFamily", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveModelName", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveSerialnumber", ctypes.c_char * STR_BUFFER_SIZE),
("caDriveReHddVersion", ctypes.c_char * STR_BUFFER_SIZE),
]
try:
mq = sysv_ipc.MessageQueue(msg_queue_key, sysv_ipc.IPC_CREAT)
class TMsgQueueData(ctypes.Structure):
_fields_ = [
("msg_queue_type", ctypes.c_long),
("driveData", TDriveData),
]
# IPC bindings - enable errno support
libc = ctypes.CDLL("libc.so.6", use_errno=True)
msgget = libc.msgget
msgrcv = libc.msgrcv
msgget.argtypes = [ctypes.c_int, ctypes.c_int]
msgget.restype = ctypes.c_int
msgrcv.argtypes = [
ctypes.c_int,
ctypes.POINTER(TMsgQueueData),
ctypes.c_size_t,
ctypes.c_long,
ctypes.c_int,
]
msgrcv.restype = ctypes.c_ssize_t
def create_drive_objects(drive_info):
"""Convert dictionary to layouter-compatible DriveData and ReHddInfo objects"""
drive = layouter.DriveData(
drive_index=int(drive_info["driveIndex"]),
drive_state=drive_info["driveState"],
modelfamily=drive_info["driveModelFamily"],
modelname=drive_info["driveModelName"],
capacity=int(drive_info["driveCapacity"]),
serialnumber=drive_info["driveSerialnumber"],
power_on_hours=int(drive_info["driveHours"]),
power_cycle=int(drive_info["driveCycles"]),
smart_error_count=int(drive_info["driveErrors"]),
shred_timestamp=int(drive_info["driveShredTimestamp"]),
shred_duration=int(drive_info["driveShredDuration"]),
)
rehdd_info = layouter.ReHddInfo(
link="https://git.mosad.xyz/localhorst/reHDD",
version=drive_info["driveReHddVersion"],
)
return drive, rehdd_info
def worker(queue_id, test_mode=False):
try:
while not terminate:
if test_mode:
time.sleep(3)
drive_info = {
"driveIndex": "42",
"driveHours": 44,
"driveCycles": 45,
"driveErrors": 43,
"driveShredTimestamp": int(time.time()),
"driveShredDuration": 0,
"driveCapacity": 42,
"driveState": "shredded",
"driveModelFamily": "modelFamily",
"driveModelName": "modelName",
"driveSerialnumber": "serial",
"driveReHddVersion": "V1.1.2",
}
else:
msg = TMsgQueueData()
print("Waiting for message from queue...")
# Calculate message size - must match C++ side: sizeof(t_msgQueueData) - sizeof(long)
# This is the size of the data portion (excluding msg_queue_type)
msg_size = ctypes.sizeof(TMsgQueueData) - ctypes.sizeof(ctypes.c_long)
print(f"Message size to receive: {msg_size} bytes")
result = msgrcv(
queue_id,
ctypes.byref(msg),
msg_size,
0, # msg type (0 = get first message)
0, # flags (0 = blocking)
)
if result == -1:
err = ctypes.get_errno()
print(
f"Error reading from message queue: {os.strerror(err)} (errno: {err})"
)
break
print(f"Received {result} bytes from queue")
d = msg.driveData
drive_info = {
"driveIndex": d.caDriveIndex.decode().strip("\x00"),
"driveHours": int(d.caDriveHours.decode().strip("\x00")),
"driveCycles": int(d.caDriveCycles.decode().strip("\x00")),
"driveErrors": int(d.caDriveErrors.decode().strip("\x00")),
"driveShredTimestamp": int(
d.caDriveShredTimestamp.decode().strip("\x00")
),
"driveShredDuration": int(
d.caDriveShredDuration.decode().strip("\x00")
),
"driveCapacity": int(d.caDriveCapacity.decode().strip("\x00")),
"driveState": d.caDriveState.decode().strip("\x00"),
"driveModelFamily": d.caDriveModelFamily.decode().strip("\x00"),
"driveModelName": d.caDriveModelName.decode().strip("\x00"),
"driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"),
"driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"),
}
print(f"Received Drive Data: {drive_info}")
except Exception as e:
import traceback
print(f"Worker encountered an error: {e}")
traceback.print_exc()
def main():
while True: while True:
message, mtype = mq.receive() try:
print("") # Create or connect to the message queue with IPC_CREAT flag
#print("*** New message received ***") # This matches the C++ sender's flags (IPC_CREAT | 0666)
# print(f"Raw message: {message}") queue_id = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0o666)
if queue_id == -1:
err = ctypes.get_errno()
raise RuntimeError(
f"Failed to create/connect to the message queue: {os.strerror(err)}"
)
#uint8_t u8DriveIndex; print(f"Successfully connected to message queue (ID: {queue_id})")
#uint32_t u32DriveHours; worker(queue_id)
#uint32_t u32DriveCycles; except Exception as e:
#uint32_t u32DriveError; import traceback
#uint64_t u64DriveShredTimestamp;
#uint64_t u64DriveShredDuration; print(f"Main process encountered an error: {e}")
#uint64_t u64DriveCapacity; traceback.print_exc()
#char caDriveState[STR_BUFFER_SIZE]; time.sleep(30)
#char caDriveModelFamiliy[STR_BUFFER_SIZE];
#char caDriveModelName[STR_BUFFER_SIZE];
#char caDriveSerialnumber[STR_BUFFER_SIZE];
driveData = pycstruct.StructDef()
driveData.add('utf-8', 'driveIndex', length=str_buffer_size)
driveData.add('utf-8', 'driveHours', length=str_buffer_size)
driveData.add('utf-8', 'driveCycles', length=str_buffer_size)
driveData.add('utf-8', 'driveErrors', length=str_buffer_size)
driveData.add('utf-8', 'driveShredTimestamp', length=str_buffer_size)
driveData.add('utf-8', 'driveShredDuration', length=str_buffer_size)
driveData.add('utf-8', 'driveCapacity', length=str_buffer_size)
driveData.add('utf-8', 'driveState', length=str_buffer_size)
driveData.add('utf-8', 'driveModelFamiliy', length=str_buffer_size)
driveData.add('utf-8', 'driveModelModel', length=str_buffer_size)
driveData.add('utf-8', 'driveSerialnumber', length=str_buffer_size)
driveData.add('utf-8', 'driveReHddVersion', length=str_buffer_size)
# Dictionary representation
result = driveData.deserialize(message)
print('Dictionary object:')
print(str(result))
except sysv_ipc.ExistentialError:
print("ERROR: message queue creation failed")
if __name__ == "__main__":
main()

77
dummy_sender/main.cpp Normal file
View File

@ -0,0 +1,77 @@
/**
* @file main.cpp
* @brief Send drive data to printer service using ipc msg queue
* @author Hendrik Schutter
* @date 06.12.2025
*/
#include "main.h"
#define REHDD_VERSION "V99.99.99"
/**
* \brief app entry point
* \param void
* \return Status-Code
*/
int main(void)
{
int msqid;
std::cout << "Dummy sender for IPC queue" << std::endl;
if (-1 == (msqid = msgget((key_t)IPC_MSG_QUEUE_KEY, IPC_CREAT | 0666)))
{
std::cout << "Printer: Create msg queue failed! Error: " << strerror(errno) << std::endl;
return EXIT_FAILURE;
}
else
{
std::cout << "Printer: Created/connected to msg queue (ID: " << msqid << ")" << std::endl;
}
t_msgQueueData msgQueueData;
msgQueueData.msg_queue_type = 1;
sprintf(msgQueueData.driveData.caDriveIndex, "%i", 0);
sprintf(msgQueueData.driveData.caDriveState, "shredded");
strcpy(msgQueueData.driveData.caDriveModelFamily, "Toshiba 2.5 HDD MK..65GSSX");
strcpy(msgQueueData.driveData.caDriveModelName, "TOSHIBA MK3265GSDX");
sprintf(msgQueueData.driveData.caDriveCapacity, "%li", 343597383000LU);
strcpy(msgQueueData.driveData.caDriveSerialnumber, "YG6742U56UDRL123456789ABCDEFGJKL");
sprintf(msgQueueData.driveData.caDriveHours, "%i", 7074);
sprintf(msgQueueData.driveData.caDriveCycles, "%i", 4792);
sprintf(msgQueueData.driveData.caDriveErrors, "%i", 1);
sprintf(msgQueueData.driveData.caDriveShredTimestamp, "%li", 71718LU);
sprintf(msgQueueData.driveData.caDriveShredDuration, "%li", 81718LU);
/*
switch (drive->connectionType)
{
case Drive::USB:
strcpy(msgQueueData.driveData.caDriveConnectionType, "usb");
break;
case Drive::SATA:
strcpy(msgQueueData.driveData.caDriveConnectionType, "sata");
break;
case Drive::NVME:
strcpy(msgQueueData.driveData.caDriveConnectionType, "nvme");
break;
case Drive::UNKNOWN:
default:
strcpy(msgQueueData.driveData.caDriveConnectionType, "na");
}
*/
sprintf(msgQueueData.driveData.caDriveReHddVersion, REHDD_VERSION);
std::cout << "Sending message to queue..." << std::endl;
if (-1 == msgsnd(msqid, &msgQueueData, sizeof(t_msgQueueData) - sizeof(long), 0))
{
std::cout << "Printer: Send msg queue failed! Error: " << strerror(errno) << std::endl;
return EXIT_FAILURE;
}
else
{
std::cout << "Printer: print triggered successfully" << std::endl;
}
return EXIT_SUCCESS;
}

45
dummy_sender/main.h Normal file
View File

@ -0,0 +1,45 @@
/**
* @file main.h
* @brief Send drive data to printer service using ipc msg queue
* @author Hendrik Schutter
* @date 06.12.2025
*/
#ifndef PRINTER_H_
#define PRINTER_H_
#include <sys/ipc.h>
#include <sys/msg.h>
#include <cstring>
#include <sstream>
#include <iostream>
#include <cerrno>
#define STR_BUFFER_SIZE 64U
#define IPC_MSG_QUEUE_KEY 0x1B11193C0
typedef struct
{
char caDriveIndex[STR_BUFFER_SIZE];
char caDriveHours[STR_BUFFER_SIZE];
char caDriveCycles[STR_BUFFER_SIZE];
char caDriveErrors[STR_BUFFER_SIZE];
char caDriveShredTimestamp[STR_BUFFER_SIZE];
char caDriveShredDuration[STR_BUFFER_SIZE];
char caDriveCapacity[STR_BUFFER_SIZE];
char caDriveState[STR_BUFFER_SIZE];
// char caDriveConnectionType[STR_BUFFER_SIZE];
char caDriveModelFamily[STR_BUFFER_SIZE];
char caDriveModelName[STR_BUFFER_SIZE];
char caDriveSerialnumber[STR_BUFFER_SIZE];
char caDriveReHddVersion[STR_BUFFER_SIZE];
} t_driveData;
typedef struct
{
long msg_queue_type;
t_driveData driveData;
} t_msgQueueData;
#endif // PRINTER_H_

View File

@ -1,9 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" Author: Hendrik Schutter, localhorst@mosad.xyz """Author: Hendrik Schutter, localhorst@mosad.xyz
Date of creation: 2022/11/23 Date of creation: 2022/11/23
Date of last modification: 2025/06/15 Date of last modification: 2025/06/15
""" """
import ctypes import ctypes
@ -119,6 +119,7 @@ def create_drive_objects(drive_info):
def worker(queue_id, test_mode=False): def worker(queue_id, test_mode=False):
try: try:
while not terminate: while not terminate:
time.sleep(3)
if test_mode: if test_mode:
drive_info = { drive_info = {
"driveIndex": "42", "driveIndex": "42",
@ -167,7 +168,6 @@ def worker(queue_id, test_mode=False):
"driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"),
"driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"), "driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"),
} }
time.sleep(3)
print(f"Received Drive Data: {drive_info}") print(f"Received Drive Data: {drive_info}")
@ -204,9 +204,16 @@ def main():
return return
while True: while True:
try: try:
queue_id = msgget(MSG_QUEUE_KEY, 0) # Create or connect to the message queue with IPC_CREAT flag
# This matches the C++ sender's flags (IPC_CREAT | 0666)
queue_id = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0o666)
if queue_id == -1: if queue_id == -1:
raise RuntimeError("Failed to connect to the existing message queue.") err = ctypes.get_errno()
raise RuntimeError(
f"Failed to create/connect to the message queue: {os.strerror(err)}"
)
print(f"Successfully connected to message queue (ID: {queue_id})")
worker(queue_id) worker(queue_id)
except Exception as e: except Exception as e:
print(f"Main process encountered an error: {e}") print(f"Main process encountered an error: {e}")