Initial import
[py-rsbac] / rsbac / _utils.py
1 # -*- coding:utf-8 -*-
2
3 # py-rsbac - RSBAC Python bindings
4 # Copyright (C) 2006  Frederic Jolliton <pyrsbac@tuxee.net>
5
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 import socket
21
22 from ctypes import byref, cast, POINTER
23
24 from rsbac.errors import raiseIfError
25
26 #--[ ctypes ]-----------------------------------------------------------------
27
28 def aptr( o ) :
29     """Return a pointer to base type of ctypes array 'o'."""
30     t = o._type_
31     return cast( byref( o ) , POINTER( t ) )
32
33 def slowButCorrectStringAssignation( object , field , data , nul = 1 ) :
34     """Carefully store a Python string into a ctypes field.
35
36     This function is obviously a temporary solution. We should find a
37     way to correctly put a string into a c_char array field in a
38     structure (or union) with always a NUL character at the end of the
39     string. Currently, ctypes doesn't report error if the string fill
40     the array completly and if there is no room to put the NUL
41     character. Also, a NUL character is silently interpreted as the
42     end of the string!
43
44     """
45     if '\x00' in data :
46         raise RuntimeError , 'a string intended for ctypes cannot contains the NUL character.'
47     size = getattr( object , '_sz_' + field , None )
48     if size is None :
49         cls = object.__class__
50         for name , desc in cls._fields_ :
51             if name == field :
52                 size = desc._length_
53                 setattr( cls , '_sz_' + field , size )
54                 break
55         else :
56             raise RuntimeError , 'unknown field %r in object of class %r' % ( field , cls )
57     if len( data ) > size - nul :
58         raise ValueError , '[XXX] string too long (%d, maximum length %d)' % ( len( data ) , size - nul )
59     setattr( object , field , data )
60
61 def stringToByteArray( array , string , fill = 0 , minPadding = 1 ) :
62     """Copy a string into a ctypes array of character.
63
64     Copy the string 'string' into ctypes array 'array'. An error is
65     thrown if string is too long. Array if filled with 'fill' value if
66     string is too short.
67
68     array -- ctypes array of character
69     string -- string to store in the array
70     fill -- fill value to use if string is smaller than the array size
71     minPadding -- mininum number of fill values to store at the end of
72     the array
73
74     Returns the first parameter.
75
76     """
77     a = len( array )
78     b = len( string )
79     if b > a - minPadding :
80         raise RuntimeError , \
81             'string of length %d cannot fit in buffer on length %d (%d)' \
82             % ( b , a - minPadding , a )
83     m = min( a , b )
84     i = 0
85     while i < m :
86         array[ i ] = ord( string[ i ] )
87         i += 1
88     while i < a :
89         array[ i ] = fill
90         i += 1
91     return array
92
93 def byteArrayToString( array ) :
94     """Read a string from a ctypes array of character.
95
96     array -- ctypes array of character
97
98     Returns a string.
99
100     """
101     end = -1
102     for end in range( 0 , len( array ) ) :
103         if not array[ end ] :
104             break
105     else :
106         end += 1
107     if end >= 0 :
108         return ''.join( map( chr , array[ : end ] ) )
109     else :
110         return ''.join( map( chr , array ) )
111
112 #--[ rsbac ]------------------------------------------------------------------
113
114 # The fetch function take care of extracting all the value from a
115 # RSBAC list. Several attempts are made if the list is updated while
116 # we try to read it.
117 #
118 # Say that L = [a,b,d]. We ask for the length and we get 3, but in the
119 # meantime, L becomes [a,b,c,d]. Then if we ask for 3 elements, we get
120 # [a,b,c].. missing one item!
121 #
122 # Internall, RSBAC tools prevent this by adding a constant amount of
123 # place but it's not perfect.
124 #
125 # Note that if the list is updated concurrently, then we can't be sure
126 # that it's up-to-date. Transaction may helps though.
127 #
128 # So, the only thing that is guaranteed, is that the list is complete
129 # according to the state at the time the request was made.
130
131 # FIXME: For multiple type, allows None to don't request a particular
132 # array?
133
134 def fetch( type , fun , start = None ) :
135     """Fetch RSBAC array.
136
137     Fetch one or several RSBAC arrays of types specified by 'type', by
138     calling the function 'fun'. This function should takes several
139     arguments: the first argument receive the number of items to
140     retrieve, and the remaining argument are ctypes pointer to already
141     allocated arrays. This function act as a callback called by fetch,
142     and may be called an unspecified number of times.
143
144     type -- either a single ctypes type, or a tuple of ctypes type
145     fun -- function taking at least 2 arguments
146     start -- None or an integer
147
148     Returns a single list if 'type' was a ctypes type, or a tuple of
149     list if 'type' was a tuple of ctypes type.
150
151     """
152     if start is None :
153         if not isinstance( type , ( tuple , list ) ) :
154             n = raiseIfError( fun( 0 , None ) )
155         else :
156             n = raiseIfError( fun( 0 , *( [ None ] * len( type ) ) ) )
157     else :
158         n = max( start , 1 )
159     if not isinstance( type , ( tuple , list ) ) :
160         while 1 :
161             # The only way to ensure that we got all the items is to ask
162             # for more and check if the result contains less than asked.
163             # If not, we increase the buffer size and we try again.
164             n += 1
165             arr = ( type * n )()
166             m = raiseIfError( fun( n , aptr( arr ) ) )
167             if m < n :
168                 # If no items are created while calling rsbac_rc_get_list,
169                 # then we will exit this loop at the first iteration.
170                 break
171             # Exponentially raise the size of the array.
172             n += n // 2 + 1
173         return arr[ : m ]
174     else :
175         types = type
176         while 1 :
177             # The only way to ensure that we got all the items is to ask
178             # for more and check if the result contains less than asked.
179             # If not, we increase the buffer size and we try again.
180             n += 1
181             arrs = [ ( type * n )() for type in types ]
182             m = raiseIfError( fun( n , *map( aptr , arrs ) ) )
183             if m < n :
184                 # If no items are created while calling rsbac_rc_get_list,
185                 # then we will exit this loop at the first iteration.
186                 break
187             # Exponentially raise the size of the array.
188             n += n // 2 + 1
189         return tuple( arr[ : m ] for arr in arrs )
190
191 def ttlToInt( ttl ) :
192     if ttl is None :
193         ttl = 0
194     else :
195         ttl = int( ttl )
196         if ttl < 1 :
197             ttl = 1
198     return ttl
199
200 def intToTtl( n ) :
201     if not n :
202         return None
203     else :
204         assert n >= 1
205         return int( n )
206
207 #-----------------------------------------------------------------------------
208
209 # FIXME: endianess?
210 def intToIp( n ) :
211     """Convert an IP represented as an integer to a tuple of integer.
212
213     intToIp(3232235821) => (192, 168, 1, 45)
214
215     """
216     n = socket.htonl( n )
217     return ( int( ( n >> 24 ) & 0xff ) ,
218              int( ( n >> 16 ) & 0xff ) ,
219              int( ( n >>  8 ) & 0xff ) ,
220              int(   n         & 0xff ) )
221
222 # FIXME: endianess?
223 def ipToInt( ip ) :
224     """Convert an IP represented as a tuple of integer to an integer.
225
226     ipToInt((192, 168, 1, 45)) => 3232235821
227
228     """
229     ip = socket.ntohl( ip )
230     if isinstance( ip , tuple ) :
231         return reduce( lambda a , b : ( a << 8 ) | b , ip , 0 )
232     elif isinstance( ip , ( int , long ) ) :
233         return ip & 0xffffffff
234     else :
235         return ip
236
237 def processName( pid ) :
238     """Get the name of a process.
239
240     Note: Extract the process name from /proc.
241
242     pid -- process ID (PID)
243
244     Returns the name of the process as string.
245
246     """
247     if isinstance( pid , int ) and pid > 0 :
248         try :
249             f = file( '/proc/%d/stat' % pid )
250             line = f.readline()
251             f.close()
252             return line[ line.find( '(' ) + 1 : line.rfind( ')' ) ]
253         except :
254             pass
255
256 def digits( n , base = 10 , size = None ) :
257     """Extract the digits from the integer 'n'.
258
259     Extract all digits (unless size is not None) in base 'base' from
260     integer 'n'. If 'size' is not None, then exactly 'size' digits are
261     extracted, adding heading 0s if necessary.
262
263     n -- integer
264     base -- integer (>= 2)
265     size -- None or an integer
266
267     Returns an array of integer.
268
269     digits(30, 4) => (1, 3, 2) because 30 is written 132 is base 4.
270
271     digits(30, 4, 6) => (0, 0, 0, 1, 3, 2)
272
273     digits(30, 4, 2) => (3, 2)  Note that a digit is missing, since we asked
274                                 only for 2.
275     """
276     r = []
277     if n < 0 :
278         n = -n
279     if size is None :
280         while 1 :
281             r.append( int( n % base ) )
282             n //= base
283             if not n :
284                 break
285     else :
286         for i in range( size ) :
287             r.append( int( n % base ) )
288             n //= base
289             if not n :
290                 r += ( 0 , ) * ( size - i - 1 )
291                 break
292     r.reverse()
293     return tuple( r )
294
295 # Local Variables:
296 # indent-tabs-mode: nil
297 # python-indent: 4
298 # End: