Help with PAM and ctypes
Lenard Lindstrom
len-l at telus.net
Fri Jun 15 03:50:21 EDT 2007
Chris AtLee wrote:
> On Jun 11, 6:01 pm, Lenard Lindstrom <l... at telus.net> wrote:
> [snip snip snip]
>>> if __name__ == "__main__":
>>> import getpass, os, sys
>>> @conv_func
>>> def my_conv(nMessages, messages, pResponse, appData):
>>> # Create an array of nMessages response objects
>>> # Does r get GC'ed after we're all done?
>>> r = (pam_response * nMessages)()
>> The memory allocated to r is garbage collected immediately after my_conv
>> returns. You need to allocate it explicitly using C's calloc or such.
>> This assumes pam_start will free the memory for you.
>>
>> addr = calloc(sizeof(pam_response), nMessages)
>>
>> addr is the memory address as a Python integer.
>>
>>> pResponse.contents = cast(r, POINTER(pam_response))
>> pResponse.contents changes the actual value of pResponse, a value on the
>> stack. You want to change the value of the pointer pResponse points to:
>>
>> pResponse[0] = cast(addr, POINTER(pam_response))
>>
>> The cast creates a POINTER(pam_reponse) instance with value addr.
>
> Ahhh, thank you! I never understood how ctypes' pointer.contents
> related
> to C pointers. So, the following are equivalent?
>
> int v = 42; v = 42
> int *p = 0; p = cast(0, POINTER(c_int))
> p = &v; p.contents = v
> *p = 123; p[0] = 123
Actually
int v = 42; v = c_int(42)
for p.contents = v to work. 42 is a Python integer. c_int(42) refers to
a mutable C integer in memory initialized to 42. Also, the preferred way
to create a NULL pointer in ctypes is to call the pointer type without
an argument:
int *p = 0; p = POINTER(c_int)()
>
> Using "pResponse[0] = cast(...)" got me part of the way to fixing my
> problem. PAM started either crashing or saying authentication had
> failed.
> The crash was in free(), and some digging around revealed that PAM
> tries to
> free the response sent by the application, which makes sense.
>
> My initial attempt to fix this involved wrapping strdup to allocate a
> new
> copy of a string to send back to PAM. Here's how I wrapped it:
>
> strdup = libc.strdup
> strdup.argstypes = [c_char_p]
> strdup.restype = c_char_p
>
> This still crashed in free(). I took a look at some of the ctypes
> regression tests and something made me try this:
>
> strdup = libc.strdup
> strdup.argstypes = [c_char_p]
> strdup.restype = POINTER(c_char) # NOT c_char_p !!!!
>
> This works like a charm. Not sure why though...Does ctypes do
> something
> special with c_char_p return types? It seems as if Python was freeing
> the
> result of strdup() when the string was GC'ed, and then PAM would fail
> when
> trying to free the same memory.
>
c_char_p implicitly converts to/from a Python string. So strdup with a
c_char_p restype returns a copy of the returned C string as a Python
string instead of a pointer to the memory allocated by strdup. And I
don't know how a Python string is cast to a c_char_p.
--
Lenard Lindstrom
<len-l at telus.net>
More information about the Python-list
mailing list