tmpfile() on windows 7 x64 - c

Running the following code on Windows 7 x64
#include <stdio.h>
#include <errno.h>
int main() {
int i;
FILE *tmp;
for (i = 0; i < 10000; i++) {
errno = 0;
if(!(tmp = tmpfile())) printf("Fail %d, err %d\n", i, errno);
fclose(tmp);
}
return 0;
}
Gives errno 13 (Permission denied), on the 637th and 1004th call, it works fine on XP (haven't tried 7 x86). Am I missing something or is this a bug?

I've got similar problem on Windows 8 - tmpfile() was causing win32 ERROR_ACCESS_DENIED error code - and yes, if you run application with administrator privileges - then it works fine.
I guess problem is mentioned over here:
https://lists.gnu.org/archive/html/bug-gnulib/2007-02/msg00162.html
Under Windows, the tmpfile function is defined to always create
its temporary file in the root directory. Most users don't have
permission to do that, so it will often fail.
I would suspect that this is kinda incomplete windows port issue - so this should be an error reported to Microsoft. (Why to code tmpfile function if it's useless ?)
But who have time to fight with Microsoft wind mills ?! :-)
I've coded similar implementation using GetTempPathW / GetModuleFileNameW / _wfopen. Code where I've encountered this problem came from libjpeg - I'm attaching whole source code here, but you can pick up code from jpeg_open_backing_store.
jmemwin.cpp:
//
// Windows port for jpeg lib functions.
//
#define JPEG_INTERNALS
#include <Windows.h> // GetTempFileName
#undef FAR // Will be redefined - disable warning
#include "jinclude.h"
#include "jpeglib.h"
extern "C" {
#include "jmemsys.h" // jpeg_ api interface.
//
// Memory allocation and freeing are controlled by the regular library routines malloc() and free().
//
GLOBAL(void *) jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject)
{
return (void *) malloc(sizeofobject);
}
GLOBAL(void) jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject)
{
free(object);
}
/*
* "Large" objects are treated the same as "small" ones.
* NB: although we include FAR keywords in the routine declarations,
* this file won't actually work in 80x86 small/medium model; at least,
* you probably won't be able to process useful-size images in only 64KB.
*/
GLOBAL(void FAR *) jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject)
{
return (void FAR *) malloc(sizeofobject);
}
GLOBAL(void) jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject)
{
free(object);
}
//
// Used only by command line applications, not by static library compilation
//
#ifndef DEFAULT_MAX_MEM /* so can override from makefile */
#define DEFAULT_MAX_MEM 1000000L /* default: one megabyte */
#endif
GLOBAL(long) jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, long max_bytes_needed, long already_allocated)
{
// jmemansi.c's jpeg_mem_available implementation was insufficient for some of .jpg loads.
MEMORYSTATUSEX status = { 0 };
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
if( status.ullAvailPhys > LONG_MAX )
// Normally goes here since new PC's have more than 4 Gb of ram.
return LONG_MAX;
return (long) status.ullAvailPhys;
}
/*
Backing store (temporary file) management.
Backing store objects are only used when the value returned by
jpeg_mem_available is less than the total space needed. You can dispense
with these routines if you have plenty of virtual memory; see jmemnobs.c.
*/
METHODDEF(void) read_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
if (fseek(info->temp_file, file_offset, SEEK_SET))
ERREXIT(cinfo, JERR_TFILE_SEEK);
size_t readed = fread( buffer_address, 1, byte_count, info->temp_file);
if (readed != (size_t) byte_count)
ERREXIT(cinfo, JERR_TFILE_READ);
}
METHODDEF(void)
write_backing_store (j_common_ptr cinfo, backing_store_ptr info, void FAR * buffer_address, long file_offset, long byte_count)
{
if (fseek(info->temp_file, file_offset, SEEK_SET))
ERREXIT(cinfo, JERR_TFILE_SEEK);
if (JFWRITE(info->temp_file, buffer_address, byte_count) != (size_t) byte_count)
ERREXIT(cinfo, JERR_TFILE_WRITE);
// E.g. if you need to debug writes.
//if( fflush(info->temp_file) != 0 )
// ERREXIT(cinfo, JERR_TFILE_WRITE);
}
METHODDEF(void)
close_backing_store (j_common_ptr cinfo, backing_store_ptr info)
{
fclose(info->temp_file);
// File is deleted using 'D' flag on open.
}
static HMODULE DllHandle()
{
MEMORY_BASIC_INFORMATION info;
VirtualQuery(DllHandle, &info, sizeof(MEMORY_BASIC_INFORMATION));
return (HMODULE)info.AllocationBase;
}
GLOBAL(void) jpeg_open_backing_store(j_common_ptr cinfo, backing_store_ptr info, long total_bytes_needed)
{
// Generate unique filename.
wchar_t path[ MAX_PATH ] = { 0 };
wchar_t dllPath[ MAX_PATH ] = { 0 };
GetTempPathW( MAX_PATH, path );
// Based on .exe or .dll filename
GetModuleFileNameW( DllHandle(), dllPath, MAX_PATH );
wchar_t* p = wcsrchr( dllPath, L'\\');
wchar_t* ext = wcsrchr( p + 1, L'.');
if( ext ) *ext = 0;
wchar_t* outFile = path + wcslen(path);
static int iTempFileId = 1;
// Based on process id (so processes would not fight with each other)
// Based on some process global id.
wsprintfW(outFile, L"%s_%d_%d.tmp",p + 1, GetCurrentProcessId(), iTempFileId++ );
// 'D' - temporary file.
if ((info->temp_file = _wfopen(path, L"w+bD") ) == NULL)
ERREXITS(cinfo, JERR_TFILE_CREATE, "");
info->read_backing_store = read_backing_store;
info->write_backing_store = write_backing_store;
info->close_backing_store = close_backing_store;
} //jpeg_open_backing_store
/*
* These routines take care of any system-dependent initialization and
* cleanup required.
*/
GLOBAL(long)
jpeg_mem_init (j_common_ptr cinfo)
{
return DEFAULT_MAX_MEM; /* default for max_memory_to_use */
}
GLOBAL(void)
jpeg_mem_term (j_common_ptr cinfo)
{
/* no work */
}
}
I'm intentionally ignoring errors from some of functions - have you ever seen GetTempPathW or GetModuleFileNameW failing ?

A bit of a refresher from the manpage of on tmpfile(), which returns a FILE*:
The file will be automatically deleted when it is closed or the program terminates.
My verdict for this issue: Deleting a file on Windows is weird.
When you delete a file on Windows, for as long as something holds a handle, you can't call CreateFile on something with the same absolute path, otherwise it will fail with the NT error code STATUS_DELETE_PENDING, which gets mapped to the Win32 code ERROR_ACCESS_DENIED. This is probably where EPERM in errno is coming from. You can confirm this with a tool like Sysinternals Process Monitor.
My guess is that CRT somehow wound up creating a file that has the same name as something it's used before. I've sometimes witnessed that deleting files on Windows can appear asynchronous because some other process (sometimes even an antivirus product, in reaction to the fact that you've just closed a delete-on-close handle...) will leave a handle open to the file, so for some timing window you will see a visible file that you can't get a handle to without hitting delete pending/access denied. Or, it could be that tmpfile has simply chosen a filename that some other process is working on.
To avoid this sort of thing you might want to consider another mechanism for temp files... For example a function like Win32 GetTempFileName allows you to create your own prefix which might make a collision less likely. That function appears to resolve race conditions by retrying if a create fails with "already exists", so be careful about deleting the temp filenames that thing generates - deleting the file cancels your rights to use it concurrently with other processes/threads.

Related

WindowsAPI GetDeviceDriverFileName( ) is not giving the full path [duplicate]

I'm working on reporting some information gleaned from native system APIs. (I know this is bad.... but I'm getting information that I can't get otherwise, and I have little issue with having to update my app if/when that time comes around.)
The native API returns native pathnames, as seen by ob, i.e. \SystemRoot\System32\Ntoskrnl.exe, or \??\C:\Program Files\VMWare Workstation\vstor-ws60.sys.
I can replace common prefixes, i.e.
std::wstring NtPathToWin32Path( std::wstring ntPath )
{
if (boost::starts_with(ntPath, L"\\\\?\\"))
{
ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
return ntPath;
}
if (boost::starts_with(ntPath, L"\\??\\"))
{
ntPath.erase(ntPath.begin(), ntPath.begin() + 4);
}
if (boost::starts_with(ntPath, L"\\"))
{
ntPath.erase(ntPath.begin(), ntPath.begin() + 1);
}
if (boost::istarts_with(ntPath, L"globalroot\\"))
{
ntPath.erase(ntPath.begin(), ntPath.begin() + 11);
}
if (boost::istarts_with(ntPath, L"systemroot"))
{
ntPath.replace(ntPath.begin(), ntPath.begin() + 10, GetWindowsPath());
}
if (boost::istarts_with(ntPath, L"windows"))
{
ntPath.replace(ntPath.begin(), ntPath.begin() + 7, GetWindowsPath());
}
return ntPath;
}
TEST(Win32Path, NtPathDoubleQuestions)
{
ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\??\\C:\\Example"));
}
TEST(Win32Path, NtPathUncBegin)
{
ASSERT_EQ(L"C:\\Example", NtPathToWin32Path(L"\\\\?\\C:\\Example"));
}
TEST(Win32Path, NtPathWindowsStart)
{
ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\Windows\\Hello\\World"));
}
TEST(Win32Path, NtPathSystemrootStart)
{
ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\SystemRoot\\Hello\\World"));
}
TEST(Win32Path, NtPathGlobalRootSystemRoot)
{
ASSERT_EQ(GetCombinedPath(GetWindowsPath(), L"Hello\\World"), NtPathToWin32Path(L"\\globalroot\\SystemRoot\\Hello\\World"));
}
but I'd be strongly surprised if there's not some API, native or otherwise, which will convert these into Win32 path names. Does such an API exist?
We do this in production code. As far as I know there is no API (public or private) that handles this. We just do some string comparisons with a few prefixes and it works for us.
Apparently there is a function named RtlNtPathNameToDosPathName() in ntdll.dll (introduced with XP?), but I have no idea what it does; I would guess it has more to do with stuff like \Device\Harddisk0, though.
I'm not sure there is really a need for such a function, though. Win32 passes paths (in the sense of CreateFile, etc) to NT; NT doesn't pass paths to Win32. So ntdll.dll doesn't really have a need to go from NT paths to Win32 paths. In the rare case where some NT query function returns a full path, any conversion function could be internal to the Win32 dll (e.g. not exported). I don't even know if they bother, as stuff like GetModuleFileName() will just return whatever path was used to load the image. I guess this is just a leaky abstraction.
Here's something you could try. First use NtCreateFile to open the file, volume etc. for reading. Then use the returned HANDLE to get the full path as described here.
This is a bit late, but I will still post my answer since even today this is a very good question!
I will share one of my functions tested and used for converting NT to DOS path. In my case, I also had to convert from ANSI to UNICODE so this is a small bonus for you to see and understand how this can be done.
All this code can be used in User Mode, so we need to first prepare some things.
Definitions & Structures:
typedef NTSTATUS(WINAPI* pRtlAnsiStringToUnicodeString)(PUNICODE_STRING, PANSI_STRING, BOOL);
typedef struct _RTL_BUFFER {
PUCHAR Buffer;
PUCHAR StaticBuffer;
SIZE_T Size;
SIZE_T StaticSize;
SIZE_T ReservedForAllocatedSize; // for future doubling
PVOID ReservedForIMalloc; // for future pluggable growth
} RTL_BUFFER, * PRTL_BUFFER;
typedef struct _RTL_UNICODE_STRING_BUFFER {
UNICODE_STRING String;
RTL_BUFFER ByteBuffer;
UCHAR MinimumStaticBufferForTerminalNul[sizeof(WCHAR)];
} RTL_UNICODE_STRING_BUFFER, * PRTL_UNICODE_STRING_BUFFER;
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_AMBIGUOUS (0x00000001)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_UNC (0x00000002)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_DRIVE (0x00000003)
#define RTL_NT_PATH_NAME_TO_DOS_PATH_NAME_ALREADY_DOS (0x00000004)
typedef NTSTATUS(WINAPI* pRtlNtPathNameToDosPathName)(__in ULONG Flags, __inout PRTL_UNICODE_STRING_BUFFER Path, __out_opt PULONG Disposition, __inout_opt PWSTR* FilePart);
#define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE (0x00000001)
#define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING (0x00000002)
#define RTL_DUPSTR_ADD_NULL RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
#define RTL_DUPSTR_ALLOC_NULL RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING
typedef NTSTATUS(WINAPI* pRtlDuplicateUnicodeString)(_In_ ULONG Flags, _In_ PUNICODE_STRING StringIn, _Out_ PUNICODE_STRING StringOut);
Importing functions:
pRtlAnsiStringToUnicodeString MyRtlAnsiStringToUnicodeString;
pRtlNtPathNameToDosPathName MyRtlNtPathNameToDosPathName;
pRtlDuplicateUnicodeString MyRtlDuplicateUnicodeString;
MyRtlAnsiStringToUnicodeString = (pRtlAnsiStringToUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlAnsiStringToUnicodeString");
MyRtlNtPathNameToDosPathName = (pRtlNtPathNameToDosPathName)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlNtPathNameToDosPathName");
MyRtlDuplicateUnicodeString = (pRtlDuplicateUnicodeString)GetProcAddress(GetModuleHandle("ntdll.dll"), "RtlDuplicateUnicodeString");
Helper function:
NTSTATUS NtPathNameToDosPathName(PUNICODE_STRING DosPath, PUNICODE_STRING NtPath)
{
NTSTATUS Status;
ULONG_PTR BufferSize;
PWSTR Buffer;
RTL_UNICODE_STRING_BUFFER UnicodeBuffer;
BufferSize = NtPath->MaximumLength + MAX_PATH * sizeof(WCHAR);
Buffer = (PWSTR)_alloca(BufferSize);
ZeroMemory(&UnicodeBuffer, sizeof(UnicodeBuffer));
UnicodeBuffer.String = *NtPath;
UnicodeBuffer.String.Buffer = Buffer;
UnicodeBuffer.String.MaximumLength = (USHORT)BufferSize;
UnicodeBuffer.ByteBuffer.Buffer = (PUCHAR)Buffer;
UnicodeBuffer.ByteBuffer.Size = BufferSize;
CopyMemory(Buffer, NtPath->Buffer, NtPath->Length);
MyRtlNtPathNameToDosPathName(0, &UnicodeBuffer, NULL, NULL);
return MyRtlDuplicateUnicodeString(RTL_DUPSTR_ADD_NULL, &UnicodeBuffer.String, DosPath);
}
Function usage:
UNICODE_STRING us;
UNICODE_STRING DosPath;
ANSI_STRING as;
as.Buffer = (char*)malloc(strlen(NT_PATH_FILE_OR_DIR) + 1);
strcpy(as.Buffer, NT_PATH_FILE_OR_DIR);
as.Length = as.MaximumLength = us.MaximumLength = us.Length = strlen(NT_PATH_FILE_OR_DIR);
MyRtlAnsiStringToUnicodeString(&us, &as, TRUE);
NtPathNameToDosPathName(&DosPath, &us);
As mentioned, in my case I needed to convert from ANSI to UNICODE and this might not apply for your case, thus you can remove it.
Same as above can be used to create custom functions and convert paths as needed.
Check this out for getting the canonical pathname in Win32. It may be helpful for you:
http://pdh11.blogspot.com/2009/05/pathcanonicalize-versus-what-it-says-on.html
See my answer to this question.
You'd need to first get a handle to the file at that path, and then get the Win32 path for the handle.
I wrote a function that converts different types of NT device names (filenames, COM ports, network paths, etc.) into a DOS path.
There are two functions. One converts a handle into an NT path and the other one converts this NT path into a DOS path.
Have a look here:
How to get name associated with open HANDLE
// "\Device\HarddiskVolume3" (Harddisk Drive)
// "\Device\HarddiskVolume3\Temp" (Harddisk Directory)
// "\Device\HarddiskVolume3\Temp\transparent.jpeg" (Harddisk File)
// "\Device\Harddisk1\DP(1)0-0+6\foto.jpg" (USB stick)
// "\Device\TrueCryptVolumeP\Data\Passwords.txt" (Truecrypt Volume)
// "\Device\Floppy0\Autoexec.bat" (Floppy disk)
// "\Device\CdRom1\VIDEO_TS\VTS_01_0.VOB" (DVD drive)
// "\Device\Serial1" (real COM port)
// "\Device\USBSER000" (virtual COM port)
// "\Device\Mup\ComputerName\C$\Boot.ini" (network drive share, Windows 7)
// "\Device\LanmanRedirector\ComputerName\C$\Boot.ini" (network drive share, Windwos XP)
// "\Device\LanmanRedirector\ComputerName\Shares\Dance.m3u" (network folder share, Windwos XP)
// "\Device\Afd" (internet socket)
// "\Device\Console000F" (unique name for any Console handle)
// "\Device\NamedPipe\Pipename" (named pipe)
// "\BaseNamedObjects\Objectname" (named mutex, named event, named semaphore)
// "\REGISTRY\MACHINE\SOFTWARE\Classes\.txt" (HKEY_CLASSES_ROOT\.txt)

Calling OpenSSL's PEM_write_PUBKEY || PEM_write_PrivateKey API makes the program to exit abruptly with message "no OPENSSL_Applink"

I am trying to use EVP_* APIs from OpenSSL, but I have encountered a weird strange behavior when trying to dump the Public/Private key from the EVP_PKEY struct.
Issue:: After populating the EVP_PKEY struct, on calling PEM_write_PUBKEY API (see TRIAL1), the program exits. The same happens on calling of PEM_write_PrivateKey API (see TRIAL2).
Output: I am left with a temp.pem 0 bytes file and a message on cmd prompt saying OPENSSL_Uplink(5D8C7000,08): no OPENSSL_Applink
#define TRIAL1
void InitOpenSSLLib(void)
{
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
}
int main(int argc, char** argv)
{
EVP_PKEY_CTX* ctx = NULL;
EVP_PKEY* pKeyPair = EVP_PKEY_new();
BIO *mem = BIO_new(BIO_s_mem());
FILE* fp = fopen("temp.pem", "wb");
InitOpenSSLLib();
ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, 0);
EVP_PKEY_keygen_init(ctx);
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048);
EVP_PKEY_keygen(ctx, &pKeyPair);
// Succeeds till here... all of the above called APIs return value greater than 0
#ifdef TRIAL1
// Program exits even before printing any error
if (PEM_write_PUBKEY(fp, pKeyPair) <= 0)
printf("PEM_write_PUBKEY failed\n");
#elif TRIAL2
// same behavior with this API too
PEM_write_PrivateKey(fp, pKeyPair, NULL, NULL, 0, 0, NULL);
#endif
// Tried this too.... but the control never reaches here
fflush(fp);
// Tried this too... most of the mem struct members are NULL.
EVP_PKEY_print_private(mem, pKeyPair, 0, 0);
//.... Cleanup codes
fclose(fp);
BIO_free_all(mem);
return 0;
}
Any pointers? Am I doing anything wrong here? Is there any other way to dump private/public key or the pair PEM format to a file?
I am using VC++ 2015. Also, on hitting Ctrl+F5, the prompt shows the message:: OPENSSL_Uplink(5D8C7000,08): no OPENSSL_Applink
Answering my own question for future devs
The key question here is the error (read, message) thrown by OpenSSL, i.e., no OPENSSL_Applink.
As documented here,
As per 0.9.8 the above limitation is eliminated for .DLLs. OpenSSL
.DLLs compiled with some specific run-time option [we insist on the
default /MD] can be deployed with application compiled with different
option or even different compiler. But there is a catch! Instead of
re-compiling OpenSSL toolkit, as you would have to with prior
versions, you have to compile small C snippet with compiler and/or
options of your choice. The snippet gets installed as
/include/openssl/applink.c and should be either added to
your application project or simply #include-d in one [and only one] of
your application source files. Failure to link this shim module into
your application manifests itself as fatal "no OPENSSL_Applink"
run-time error. An explicit reminder is due that in this situation
[mixing compiler options] it is as important to add CRYPTO_malloc_init
prior first call to OpenSSL.
you should check your compiling options for /MD (assuming you know the Code Generation options under VC++ Project Properties).
I did the same, but still my issue didn't get resolved. The answer to this is the second paragraph to the link given [https://www.openssl.org/docs/faq.html#PROG2], where it instructs us to include <install-root>/include/openssl/applink.c
Where to get this applink.c file if not found in openssl's
dir?
In my case, I installed OpenSSL using exe binary and I couldn't find this specific applink.c file anywhere in my <install-root>, so I started looking and found it here on GitHub.
All you have to do is, include this file as source file in your project or better save it in openssl install-root dir, so that you can include it from the same location for other projects too.
// applink.c from https://github.com/openssl/openssl/blob/master/ms/applink.c
/*
* Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#define APPLINK_STDIN 1
#define APPLINK_STDOUT 2
#define APPLINK_STDERR 3
#define APPLINK_FPRINTF 4
#define APPLINK_FGETS 5
#define APPLINK_FREAD 6
#define APPLINK_FWRITE 7
#define APPLINK_FSETMOD 8
#define APPLINK_FEOF 9
#define APPLINK_FCLOSE 10 /* should not be used */
#define APPLINK_FOPEN 11 /* solely for completeness */
#define APPLINK_FSEEK 12
#define APPLINK_FTELL 13
#define APPLINK_FFLUSH 14
#define APPLINK_FERROR 15
#define APPLINK_CLEARERR 16
#define APPLINK_FILENO 17 /* to be used with below */
#define APPLINK_OPEN 18 /* formally can't be used, as flags can vary */
#define APPLINK_READ 19
#define APPLINK_WRITE 20
#define APPLINK_LSEEK 21
#define APPLINK_CLOSE 22
#define APPLINK_MAX 22 /* always same as last macro */
#ifndef APPMACROS_ONLY
# include <stdio.h>
# include <io.h>
# include <fcntl.h>
static void *app_stdin(void)
{
return stdin;
}
static void *app_stdout(void)
{
return stdout;
}
static void *app_stderr(void)
{
return stderr;
}
static int app_feof(FILE *fp)
{
return feof(fp);
}
static int app_ferror(FILE *fp)
{
return ferror(fp);
}
static void app_clearerr(FILE *fp)
{
clearerr(fp);
}
static int app_fileno(FILE *fp)
{
return _fileno(fp);
}
static int app_fsetmod(FILE *fp, char mod)
{
return _setmode(_fileno(fp), mod == 'b' ? _O_BINARY : _O_TEXT);
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport)
void **
# if defined(__BORLANDC__)
/*
* __stdcall appears to be the only way to get the name
* decoration right with Borland C. Otherwise it works
* purely incidentally, as we pass no parameters.
*/
__stdcall
# else
__cdecl
# endif
OPENSSL_Applink(void)
{
static int once = 1;
static void *OPENSSL_ApplinkTable[APPLINK_MAX + 1] =
{ (void *)APPLINK_MAX };
if (once) {
OPENSSL_ApplinkTable[APPLINK_STDIN] = app_stdin;
OPENSSL_ApplinkTable[APPLINK_STDOUT] = app_stdout;
OPENSSL_ApplinkTable[APPLINK_STDERR] = app_stderr;
OPENSSL_ApplinkTable[APPLINK_FPRINTF] = fprintf;
OPENSSL_ApplinkTable[APPLINK_FGETS] = fgets;
OPENSSL_ApplinkTable[APPLINK_FREAD] = fread;
OPENSSL_ApplinkTable[APPLINK_FWRITE] = fwrite;
OPENSSL_ApplinkTable[APPLINK_FSETMOD] = app_fsetmod;
OPENSSL_ApplinkTable[APPLINK_FEOF] = app_feof;
OPENSSL_ApplinkTable[APPLINK_FCLOSE] = fclose;
OPENSSL_ApplinkTable[APPLINK_FOPEN] = fopen;
OPENSSL_ApplinkTable[APPLINK_FSEEK] = fseek;
OPENSSL_ApplinkTable[APPLINK_FTELL] = ftell;
OPENSSL_ApplinkTable[APPLINK_FFLUSH] = fflush;
OPENSSL_ApplinkTable[APPLINK_FERROR] = app_ferror;
OPENSSL_ApplinkTable[APPLINK_CLEARERR] = app_clearerr;
OPENSSL_ApplinkTable[APPLINK_FILENO] = app_fileno;
OPENSSL_ApplinkTable[APPLINK_OPEN] = _open;
OPENSSL_ApplinkTable[APPLINK_READ] = _read;
OPENSSL_ApplinkTable[APPLINK_WRITE] = _write;
OPENSSL_ApplinkTable[APPLINK_LSEEK] = _lseek;
OPENSSL_ApplinkTable[APPLINK_CLOSE] = _close;
once = 0;
}
return OPENSSL_ApplinkTable;
}
#ifdef __cplusplus
}
#endif
#endif
That's it, this will eliminate the strange behavior of program exit.

getpwuid_r and getgrgid_r are slow, how can I cache them?

I am writing a clone of find while learning C. When implementing the -ls option I've stumbled upon a problem that the getpwuid_r and getgrgid_r calls are really slow, the same applies to getpwuid and getgrgid. I need them to display the user/group names from ids provided by stat.h.
For example, listing the whole filesystem gets 3x slower:
# measurements were made 3 times and the fastest run was recorded
# with getgrgid_r
time ./myfind / -ls > list.txt
real 0m4.618s
user 0m1.848s
sys 0m2.744s
# getgrgid_r replaced with 'return "user";'
time ./myfind / -ls > list.txt
real 0m1.437s
user 0m0.572s
sys 0m0.832s
I wonder how GNU find maintains such a good speed. I've seen the sources, but they are not exactly easy to understand and to apply without special types, macros etc:
time find / -ls > list.txt
real 0m1.544s
user 0m0.884s
sys 0m0.648s
I thought about caching the uid - username and gid - groupname pairs in a data structure. Is it a good idea? How would you implement it?
You can find my complete code here.
UPDATE:
The solution was exactly what I was looking for:
time ./myfind / -ls > list.txt
real 0m1.480s
user 0m0.696s
sys 0m0.736s
Here is a version based on getgrgid (if you don't require thread safety):
char *do_get_group(struct stat attr) {
struct group *grp;
static unsigned int cache_gid = UINT_MAX;
static char *cache_gr_name = NULL;
/* skip getgrgid if we have the record in cache */
if (cache_gid == attr.st_gid) {
return cache_gr_name;
}
/* clear the cache */
cache_gid = UINT_MAX;
grp = getgrgid(attr.st_gid);
if (!grp) {
/*
* the group is not found or getgrgid failed,
* return the gid as a string then;
* an unsigned int needs 10 chars
*/
char group[11];
if (snprintf(group, 11, "%u", attr.st_gid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return "";
}
return group;
}
cache_gid = grp->gr_gid;
cache_gr_name = grp->gr_name;
return grp->gr_name;
}
getpwuid:
char *do_get_user(struct stat attr) {
struct passwd *pwd;
static unsigned int cache_uid = UINT_MAX;
static char *cache_pw_name = NULL;
/* skip getpwuid if we have the record in cache */
if (cache_uid == attr.st_uid) {
return cache_pw_name;
}
/* clear the cache */
cache_uid = UINT_MAX;
pwd = getpwuid(attr.st_uid);
if (!pwd) {
/*
* the user is not found or getpwuid failed,
* return the uid as a string then;
* an unsigned int needs 10 chars
*/
char user[11];
if (snprintf(user, 11, "%u", attr.st_uid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return "";
}
return user;
}
cache_uid = pwd->pw_uid;
cache_pw_name = pwd->pw_name;
return pwd->pw_name;
}
UPDATE 2:
Changed long to unsigned int.
UPDATE 3:
Added the cache clearing. It is absolutely necessary, because pwd->pw_name may point to a static area. getpwuid can overwrite its contents if it fails or simply when executed somewhere else in the program.
Also removed strdup. Since the output of getgrgid and getpwuid should not be freed, there is no need to require free for our wrapper functions.
The timings indeed indicate a strong suspicion on these functions.
Looking at your function do_get_group, there are some issues:
You use sysconf(_SC_GETPW_R_SIZE_MAX); for every call to do_get_group and do_get_user, definitely cache that, it will not change during the lifetime of your program, but you will not gain much.
You use attr.st_uid instead of attr.st_gid, which probably causes the lookup to fail for many files, possibly defeating the cacheing mechanism, if any. Fix this first, this is a bug!
You return values that should not be passed to free() by the caller, such as grp->gr_name and "". You should always allocate the string you return. The same issue is probably present in do_get_user().
Here is a replacement for do_get_group with a one shot cache. See if this improves the performance:
/*
* #brief returns the groupname or gid, if group not present on the system
*
* #param attr the entry attributes from lstat
*
* #returns the groupname if getgrgid() worked, otherwise gid, as a string
*/
char *do_get_group(struct stat attr) {
char *group;
struct group grp;
struct group *result;
static size_t length = 0;
static char *buffer = NULL;
static gid_t cache_gid = -1;
static char *cache_gr_name = NULL;
if (!length) {
/* only allocate the buffer once */
long sysconf_length = sysconf(_SC_GETPW_R_SIZE_MAX);
if (sysconf_length == -1) {
sysconf_length = 16384;
}
length = (size_t)sysconf_length;
buffer = calloc(length, 1);
}
if (!buffer) {
fprintf(stderr, "%s: malloc(): %s\n", program, strerror(errno));
return strdup("");
}
/* check the cache */
if (cache_gid == attr.st_gid) {
return strdup(cache_gr_name);
}
/* empty the cache */
cache_gid = -1;
free(cache_gr_name);
cache_gr_name = NULL;
if (getgrgid_r(attr.st_gid, &grp, buffer, length, &result) != 0) {
fprintf(stderr, "%s: getpwuid_r(): %s\n", program, strerror(errno));
return strdup("");
}
if (result) {
group = grp.gr_name;
} else {
group = buffer;
if (snprintf(group, length, "%ld", (long)attr.st_gid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return strdup("");
}
}
/* load the cache */
cache_gid = attr.st_gid;
cache_gr_name = strdup(group);
return strdup(group);
}
Whether the getpwuid and getgrgid calls are cached depends on how they are implemented and on how your system is configured. I recently wrote an implementation of ls and ran into a similar problem.
I found that on all modern systems I tested, the two functions are uncached unless you run the name service caching dæmon (nscd) in which case nscd makes sure that the cache stays up to date. It's easy to understand why this happens: Without an nscd, caching the information could lead to outdated output which is a violation of the specification.
I don't think you should rely on these functions caching the group and passwd databases because they often don't. I implemented custom caching code for this purpose. If you don't require to have up-to-date information in case the database contents change during program execution, this is perfectly fine to do.
You can find my implementation of such a cache here. I'm not going to publish it on Stack Overflow as I do not desire to publish the code under the MIT license.

Configure Parameters of LED Trigger from Kernel Space

I'm working on an embedded project. Our board is using Linux kernel v3.16.7. I'm working on supporting a couple of peripheral LEDs that monitor activity. I've successfully modified the boot procedure to load the drivers and create sysfs entries in /sys/class/leds/, which is great. I've also attached a oneshot trigger to the leds so I can echo 1 > shot from within /sys/class/leds/actled1\:green/ and the led blinks. Exactly what I want.
However, I want to configure the delays for each LED when I instantiate the driver during boot, and I'm not clear on how to do that. The driver creates sysfs entries in /sys/class/leds/actled1\:green/ called delay_on and delay_off, and I can write to them from userspace to configure the delays, but it should be possible to set their initial values from from kernel space during instantiation. I also want to be able to set the invert parameter (which is just another sysfs entry just like the delays).
How can I configure the parameters of an led trigger when I instantiate the driver from kernel space?
Below is how I instantiate the LED GPIOs. First I set up the structs required:
static struct gpio_led my_leds[] __initdata = {
{
.name = "actled1:green",
.default_trigger = "oneshot"
.gpio = ACTIVITY_LED_GPIO_BASE + 0,
.active_low = true,
},
{
.name = "actled2:red",
.default_trigger = "oneshot"
.gpio = ACTIVITY_LED_GPIO_BASE + 1,
.active_low = true,
},
};
static struct gpio_led_platform_data my_leds_pdata __initdata = {
.num_leds = ARRAY_SIZE(my_leds),
.leds = my_leds,
};
Then, I call this function to create the platform devices:
static int __init setup_my_leds (void)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_alloc("leds-gpio", -1);
if (!pdev) {
return -ENOMEM;
}
ret = platform_device_add_data(pdev,
&my_leds_pdata,
sizeof(my_leds_pdata));
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
ret = platform_device_add(pdev);
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
return 0;
}
The definition for the gpio_led struct is in include/linux/leds.h line 327, and the definition for gpio_led_platform_data is in line 341 of the same file. The definition of platform_device_add_data is in drivers/base/platform.c line 284.
It may be useful to look at the source for the oneshot trigger (drivers/leds/trigger/ledtrig-oneshot.c) in order to answer the question. Also relevant is the "leds-gpio" driver (drivers/leds/leds-gpio.c).
I suspect the answer is somewhere in drivers/base/platform.c and the associated documentation, but I'm not seeing any functions that deal with the data I need.
To address some of the information that I inadvertently left out:
the boot loader sets the kernel arguments, and we can't modify the boot loader. that's fine; the values i want to set are constants and i can hard-code them in.
the driver is baked into the kernel at compile time (and, i presume, loaded by the bootloader) rather than loading a .ko with modprobe later.
i would love a general way to set arbitrary trigger parameters, not only oneshot's delay_on / delay_off. for example, oneshot's invert parameter.
i'm totally fine modifying oneshot / creating new triggers. in fact, once i get it working with oneshot, i'll need to create a new trigger that expands upon oneshot (which is also the reason i need to set arbitrary parameters).
There are a few issues and I think I've found the solutions, but even though you provided a good deal of info, there were some things missing, so I'll enumerate for all possible scenarios, so be patient ...
(1) Getting the initial values you want to set. I presume you have already figured this out, but ... You can get these from kernel cmdline parsing (e.g. you add the values to /boot/grub2/grub.cfg as myleds.delay_on=.... If you're loading via modprobe, you set a module parameter. These could also be a config file as in myleds.config_file=/etc/sysconfig/myleds.conf
(2) You could set them inside your setup_my_leds [except for the recalcitrance of oneshot_trig_activate--which we'll deal with soon enough]. From drivers/base/platform.c:
/**
* arch_setup_pdev_archdata - Allow manipulation of archdata before its used
* #pdev: platform device
*
* This is called before platform_device_add() such that any pdev_archdata may
* be setup before the platform_notifier is called. So if a user needs to
* manipulate any relevant information in the pdev_archdata they can do:
*
* platform_device_alloc()
* ... manipulate ...
* platform_device_add()
*
* And if they don't care they can just call platform_device_register() and
* everything will just work out.
*/
So, with that in mind, let's change your setup function slightly:
static int __init setup_my_leds (void)
{
struct platform_device *pdev;
int ret;
// get initial values you want to set, possibly storing away for later use
my_leds_get_init_values(...);
pdev = platform_device_alloc("leds-gpio", -1);
if (!pdev) {
return -ENOMEM;
}
// Choice (1): set your initial values in my_leds_pdata here
my_leds_set_init_values(&my_leds_pdata);
// NOTE: just does kmemdup and sets pdev->dev.platform_data
ret = platform_device_add_data(pdev,
&my_leds_pdata,
sizeof(my_leds_pdata));
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
// Choice (2): set your initial values in pdev->dev.platform_data here
my_leds_set_init_values(pdev->dev.platform_data);
ret = platform_device_add(pdev);
if (ret < 0) {
platform_device_put(pdev);
return ret;
}
return 0;
}
(3) Unfortunately, since you're using .default_trigger = "oneshot", the above data will get blasted by oneshot_trig_activate in drivers/leds/trigger/ledtrig-oneshot.c. So, we need to deal with that.
Option (A): Assuming you can rebuild the whole kernel as you choose, just modify oneshot_trig_activate in ledtrig-oneshot.c and remove the the lines that use DEFAULT_DELAY. This is only really useful if you know that it's not used by anything else in your system that might need the default values.
Option (B): If you're not allowed to modify ledtrig-oneshot.c, but are allowed to add new triggers to drivers/leds/trigger, copy the file to (e.g.) ledtrig-oneshot2.c and do the changes there. You'll need to change the .name to .name = "oneshot2". The easy way [in vi, of course :-)] is :%s/oneshot/oneshot2/g. You'll also need to add a new entry in the Kconfig and Makefile for this. Then, change your struct definition to use the new driver: .default_trigger = "oneshot2"
Option (C): Assuming you can't [or don't want to] touch the drivers/leds/trigger directory, copy ledtrig-oneshot.c to your driver directory [renaming as appropriate]. Do the edits from option (B) above there. With some trickery in your Makefile, you can get it to build both my_led_driver.ko and ledtrig-oneshot2.ko. You'll need modify your Kconfig, possibly adding a depends on LED_TRIGGERS for the led trigger driver. You could also put the two into separate subdirectories and the individual Makefile/Kconfig might be simpler: my_led/my_driver and my_led/my_trigger
Option (C) would be more work up front, but might be cleaner and more portable in the long run. Of course, you could do option (A) for proof-of-concept, then do option (B), and do the "final ship" as option (C).
An alternate way for when you set the initial values: Remember the comment for my_leds_get_init_values was possibly storing away for later use. You could change oneshot2_trig_activate to call it instead of using DEFAULT_DELAY. I don't like this quite as much and prefer the solutions that simply neuter oneshot_trig_activate's offensive behavior. But, with some testing, you may find that this is the way you have to do it.
Hopefully, the above will work. If not, edit your question with additional info and/or restrictions [and send me a comment], and I'll be glad to update my answer [I've been doing drivers for 40+].
UPDATE: Okay, herein is a fully annotated and modified LED trigger driver that you can use as a drop in replacement for drivers/led/trigger/ledtrig-oneshot.c.
Because the invert parameter can not be passed directly through any standard struct you have access to in your setup function [i.e. it's stored in a private struct inside the trigger driver], remove the "Choice (1)" and "Choice (2)". We'll set them all at once inside the [modified] oneshot_trig_activate.
Also, the init parameters you want must be set up and stored as globals by the my_leds_get_init_values so the trigger driver can find them. That is, there is no way to do this cleanly (e.g. with a pointer to a private struct that gets passed around) as the structs you have access to in setup don't have a field for this. See the top part of the trigger driver for discussion on this.
My first step was to annotate the base driver with descriptive comments. There were no comments in it, except for K&R style for copyright and a single one-liner. My annotations are ANSI ("//") comments.
If I were taking over the driver, I would add these and leave them in. However, my level of comments might be considered "over-commenting" according to the kernel style guide and might be considered "cruft", particularly for a driver that is this straightforward.
Next step was to add the necessary changes. All places that have additions/changes are marked with a comment block that starts with "C:". These are the important places to look. Note that these comments are legitimate candidates to leave in. In other more complex drivers, the level of commenting is up to the author. The "C:" is just to highlight the places for you.
With the annotations, a straight line read through might be easier now. Also, a diff -u might also help. If you've got everything under git, so much the better.
Because of all this, I'd remove the "Option (A)" [direct modification of the original file] and do "Option (B)" or "Option (C)" only.
The trigger driver uses all static definitions, so the global edit I suggested before is not needed. I did do .name = "myled_oneshot";, so you'll need to match that with .default_trigger = "myled_oneshot";. Feel free to use my_leds_whatever to be consistent with your existing naming convention. When I do this for myself, I usually use my initials, so it becomes ce_leds_whatever--YMMV
Anyway, here's the entire modified trigger driver. Note that I've done the editing, but I've not tried to compile/build it.
/*
* One-shot LED Trigger
*
* Copyright 2012, Fabio Baltieri <fabio.baltieri#gmail.com>
*
* Based on ledtrig-timer.c by Richard Purdie <rpurdie#openedhand.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/leds.h>
#include "../leds.h"
// C: we need to get access to the init data populated by the setup function
// we have the "clean way" with a struct definition inside a header file and
// the "dirty way" using three separate int globals
// in either case, the externs referenced here must be defined in the "my_leds"
// driver as global
// C: the "clean way"
// (1) requires that we have a path to the .h (e.g. -I<whatever)
// (2) this would be easier/preferable for the "Option (C)"
// (3) once done, easily extensible [probably not a consideration here]
#ifdef MYLED_USESTRUCT
#include "whatever/myled_init.h"
extern struct myled_init myled_init;
// C: the "ugly way"
// (1) no need to use a separate .h file
// (2) three separate global variables is wasteful
// (3) more than three, and we really should consider the "struct"
#else
extern int myled_init_delay_on;
extern int myled_init_delay_off;
extern int myled_init_invert;
#endif
#define DEFAULT_DELAY 100
// oneshot trigger driver private data
struct oneshot_trig_data {
unsigned int invert; // current invert state
};
// arm oneshot sequence from sysfs write to shot file
static ssize_t led_shot(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
led_blink_set_oneshot(led_cdev,
&led_cdev->blink_delay_on, &led_cdev->blink_delay_off,
oneshot_data->invert);
/* content is ignored */
return size;
}
// show invert state for "cat invert"
static ssize_t led_invert_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
return sprintf(buf, "%u\n", oneshot_data->invert);
}
// set invert from sysfs write to invert file (e.g. echo 1 > invert)
static ssize_t led_invert_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
oneshot_data->invert = !!state;
if (oneshot_data->invert)
led_set_brightness_async(led_cdev, LED_FULL);
else
led_set_brightness_async(led_cdev, LED_OFF);
return size;
}
// show delay_on state for "cat delay_on"
static ssize_t led_delay_on_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}
// set delay_on from sysfs write to delay_on file (e.g. echo 20 > delay_on)
static ssize_t led_delay_on_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_on = state;
return size;
}
// show delay_off state for "cat delay_off"
static ssize_t led_delay_off_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}
// set delay_off from sysfs write to delay_off file (e.g. echo 20 > delay_off)
static ssize_t led_delay_off_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
unsigned long state;
int ret;
ret = kstrtoul(buf, 0, &state);
if (ret)
return ret;
led_cdev->blink_delay_off = state;
return size;
}
// these are the "attribute" definitions -- one for each sysfs entry
// pointers to these show up in the above functions as the "attr" argument
static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);
static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
static DEVICE_ATTR(shot, 0200, NULL, led_shot);
// activate the trigger device
static void oneshot_trig_activate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data;
int rc;
// create an instance of the private data we need
oneshot_data = kzalloc(sizeof(*oneshot_data), GFP_KERNEL);
if (!oneshot_data)
return;
// save the pointer in the led class struct so it's available to other
// functions above
led_cdev->trigger_data = oneshot_data;
// attach the sysfs entries
rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
if (rc)
goto err_out_trig_data;
rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
if (rc)
goto err_out_delayon;
rc = device_create_file(led_cdev->dev, &dev_attr_invert);
if (rc)
goto err_out_delayoff;
rc = device_create_file(led_cdev->dev, &dev_attr_shot);
if (rc)
goto err_out_invert;
// C: this is what the driver used to do
#if 0
led_cdev->blink_delay_on = DEFAULT_DELAY;
led_cdev->blink_delay_off = DEFAULT_DELAY;
#endif
led_cdev->activated = true;
// C: from here to the return is what the modified driver must do
#ifdef MYLED_USESTRUCT
led_cdev->blink_delay_on = myled_init.delay_on;
led_cdev->blink_delay_off = myled_init.delay_off;
oneshot_data->invert = myled_init.invert;
#else
led_cdev->blink_delay_on = myled_init_delay_on;
led_cdev->blink_delay_off = myled_init_delay_off;
oneshot_data->invert = myled_init_invert;
#endif
// C: if invert is off, nothing to do -- just like before
// if invert is set, we implement this as if we just got an instantaneous
// write to the sysfs "invert" file (which would call led_invert_store
// above)
// C: this is a direct rip-off of the above led_invert_store function which
// we can _not_ call here directly because we don't have access to the
// data it needs for its arguments [at least, not conveniently]
// so, we extract the one line we actually need
if (oneshot_data->invert)
led_set_brightness_async(led_cdev, LED_FULL);
return;
// release everything if an error occurs
err_out_invert:
device_remove_file(led_cdev->dev, &dev_attr_invert);
err_out_delayoff:
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
err_out_delayon:
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out_trig_data:
kfree(led_cdev->trigger_data);
}
// deactivate the trigger device
static void oneshot_trig_deactivate(struct led_classdev *led_cdev)
{
struct oneshot_trig_data *oneshot_data = led_cdev->trigger_data;
// release/destroy all the sysfs entries [and free the private data]
if (led_cdev->activated) {
device_remove_file(led_cdev->dev, &dev_attr_delay_on);
device_remove_file(led_cdev->dev, &dev_attr_delay_off);
device_remove_file(led_cdev->dev, &dev_attr_invert);
device_remove_file(led_cdev->dev, &dev_attr_shot);
kfree(oneshot_data);
led_cdev->activated = false;
}
/* Stop blinking */
led_set_brightness(led_cdev, LED_OFF);
}
// definition/control for trigger device registration
// C: changed the name to "myled_oneshot"
static struct led_trigger oneshot_led_trigger = {
.name = "myled_oneshot",
.activate = oneshot_trig_activate,
.deactivate = oneshot_trig_deactivate,
};
// module init function -- register the trigger device
static int __init oneshot_trig_init(void)
{
return led_trigger_register(&oneshot_led_trigger);
}
// module exit function -- unregister the trigger device
static void __exit oneshot_trig_exit(void)
{
led_trigger_unregister(&oneshot_led_trigger);
}
module_init(oneshot_trig_init);
module_exit(oneshot_trig_exit);
MODULE_AUTHOR("Fabio Baltieri <fabio.baltieri#gmail.com>");
MODULE_DESCRIPTION("One-shot LED trigger");
MODULE_LICENSE("GPL");
As you can see in ledtrig-oneshot.c, the delay is always initialized with DEFAULT_DELAY. Unfortunately, if you want to be able to configure a different value at startup, this is a mechanism you will have to implement..
As Craig answered it should be from kernel command line options, but there could be a problem with embedded systems where the boot-loader passes the command line parameters and the boot-loaders cannot be modified, they are usually OTP . In that case I see only 2 options
hard coding in the kernel init function
as mac address is stored in eeprom for the nic driver to read, if the values can be stored in a flash (nor) and the value read on boot. This can be done after creating the mtd partitions during kernel boot.

ALSA equivalent to /dev/audio dump?

This will be my poorest question ever...
On an old netbook, I installed an even older version of Debian, and toyed around a bit. One of the rather pleasing results was a very basic MP3 player (using libmpg123), integrated for adding background music to a little application doing something completely different. I grew rather fond of this little solution.
In that program, I dumped the decoded audio (from mpg123_decode()) to /dev/audio via a simple fwrite().
This worked fine - on the netbook.
Now, I came to understand that /dev/audio was something done by OSS, and is no longer supported on newer (ALSA) machines. Sure enough, my laptop (running a current Linux Mint) does not have this device.
So apparently I have to use ALSA instead. Searching the web, I've found a couple of tutorials, and they pretty much blow my mind. Modes, parameters, capabilities, access type, sample format, sample rate, number of channels, number of periods, period size... I understand that ALSA is a powerful API for the ambitious, but that's not what I am looking for (or have the time to grok). All I am looking for is how to play the output of mpg123_decode (the format of which I don't even know, not being an audio geek by a long shot).
Can anybody give me some hints on what needs to be done?
tl;dr
How do I get ALSA to play raw audio data?
There's an OSS compatibility layer for ALSA in the alsa-oss package. Install it and run your program inside the "aoss" program. Or, modprobe the modules listed here:
http://wiki.debian.org/SoundFAQ/#line-105
Then, you'll need to change your program to use "/dev/dsp" or "/dev/dsp0" instead of "/dev/audio". It should work how you remembered... but you might want to cross your fingers just in case.
You could install sox and open a pipe to the play command with the correct samplerate and sample size arguments.
Using ALSA directly is overly complicated, so I hope a Gstreamer solution is fine to you too. Gstreamer gives a nice abstraction to ALSA/OSS/Pulseaudio/you name it -- and is ubiquitous in the Linux world.
I wrote a little library that will open a FILE object where you can fwrite PCM data into:
Gstreamer file. The actual code is less than 100 lines.
Use use it like that:
FILE *output = fopen_gst(rate, channels, bit_depth); // open audio output file
while (have_more_data) fwrite(data, amount, 1, output); // output audio data
fclose(output); // close the output file
I added an mpg123 example, too.
Here is the whole file (in case Github get's out of business ;-) ):
/**
* gstreamer_file.c
* Copyright 2012 René Kijewski <rene.SURNAME#fu-berlin.de>
* License: LGPL 3.0 (http://www.gnu.org/licenses/lgpl-3.0)
*/
#include "gstreamer_file.h"
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <gst/gst.h>
#ifndef _GNU_SOURCE
# error "You need to add -D_GNU_SOURCE to the GCC parameters!"
#endif
/**
* Cookie passed to the callbacks.
*/
typedef struct {
/** { file descriptor to read from, fd to write to } */
int pipefd[2];
/** Gstreamer pipeline */
GstElement *pipeline;
} cookie_t;
static ssize_t write_gst(void *cookie_, const char *buf, size_t size) {
cookie_t *cookie = cookie_;
return write(cookie->pipefd[1], buf, size);
}
static int close_gst(void *cookie_) {
cookie_t *cookie = cookie_;
gst_element_set_state(cookie->pipeline, GST_STATE_NULL); /* we are finished */
gst_object_unref(GST_OBJECT(cookie->pipeline)); /* we won't access the pipeline anymore */
close(cookie->pipefd[0]); /* we won't write anymore */
close(cookie->pipefd[1]); /* we won't read anymore */
free(cookie); /* dispose the cookie */
return 0;
}
FILE *fopen_gst(long rate, int channels, int depth) {
/* initialize Gstreamer */
if (!gst_is_initialized()) {
GError *error;
if (!gst_init_check(NULL, NULL, &error)) {
g_error_free(error);
return NULL;
}
}
/* get a cookie */
cookie_t *cookie = malloc(sizeof(*cookie));
if (!cookie) {
return NULL;
}
/* open a pipe to be used between the caller and the Gstreamer pipeline */
if (pipe(cookie->pipefd) != 0) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* set up the pipeline */
char description[256];
snprintf(description, sizeof(description),
"fdsrc fd=%d ! " /* read from a file descriptor */
"audio/x-raw-int, rate=%ld, channels=%d, " /* get PCM data */
"endianness=1234, width=%d, depth=%d, signed=true ! "
"audioconvert ! audioresample ! " /* convert/resample if needed */
"autoaudiosink", /* output to speakers (using ALSA, OSS, Pulseaudio ...) */
cookie->pipefd[0], rate, channels, depth, depth);
cookie->pipeline = gst_parse_launch_full(description, NULL,
GST_PARSE_FLAG_FATAL_ERRORS, NULL);
if (!cookie->pipeline) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* open a FILE with specialized write and close functions */
cookie_io_functions_t io_funcs = { NULL, write_gst, NULL, close_gst };
FILE *result = fopencookie(cookie, "w", io_funcs);
if (!result) {
close_gst(cookie);
return NULL;
}
/* start the pipeline (of cause it will wait for some data first) */
gst_element_set_state(cookie->pipeline, GST_STATE_PLAYING);
return result;
}
And ten years later, the "actual" answer is found: That's the wrong way to do it in the first place.
libmpg123 comes with a companion library, libout123, which abstracts the underlying audio system for you. Based on libmpg123 example code:
#include <stdlib.h>
#include "mpg123.h"
#include "out123.h"
int main()
{
mpg123_handle * _mpg_handle;
out123_handle * _out_handle;
double rate, channels, encoding;
size_t position, buffer_size;
unsigned char * buffer;
char filename[] = "Example.mp3";
mpg123_open( _mpg_handle, filename );
mpg123_getformat( _mpg_handle, &rate, &channels, &encoding );
out123_open( _out_handle, NULL, NULL );
mpg123_format_none( _mpg_handle );
mpg123_format( _mpg_handle, rate, channels, encoding );
out123_start( _out_handle, rate, channels, encoding );
buffer_size = mpg123_outblock( _mpg_handle );
buffer = malloc( buffer_size );
do
{
mpg123_read( _mpg_handle, buffer.get(), buffer_size, &position );
out123_play( _out_handle, buffer.get(), position );
} while ( position );
out123_close( _out_handle );
mpg123_close( _mpg_handle );
free( buffer );
}

Resources