577ec48482a2258b0cbe273ba5d267d2b318d2ef
[ira/wip.git] / source3 / rpc_server / srv_eventlog_nt.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines
4  *  Copyright (C) Marcin Krzysztof Porwit    2005,
5  *  Copyright (C) Brian Moran                2005,
6  *  Copyright (C) Gerald (Jerry) Carter      2005.
7  *  
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *  
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *  
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 #include "includes.h"
24
25 #undef  DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
27
28 typedef struct {
29         char *logname;
30         TDB_CONTEXT *tdb;
31         uint32 num_records;
32         uint32 oldest_entry;
33         uint32 flags;
34         uint32 access_granted;
35 } EVENTLOG_INFO;
36
37 /********************************************************************
38  ********************************************************************/
39
40 static void free_eventlog_info( void *ptr )
41 {
42         EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
43         
44         if ( elog->tdb )
45                 elog_close_tdb( elog->tdb );
46         
47         TALLOC_FREE( elog );
48 }
49
50 /********************************************************************
51  ********************************************************************/
52
53 static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
54                                                 POLICY_HND * handle )
55 {
56         EVENTLOG_INFO *info;
57
58         if ( !find_policy_by_hnd( p, handle, ( void ** ) &info ) ) {
59                 DEBUG( 2,
60                        ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
61                 return NULL;
62         }
63
64         return info;
65 }
66
67 /********************************************************************
68 ********************************************************************/
69
70 static BOOL elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
71 {
72         char *tdbname = elog_tdbname( info->logname );
73         SEC_DESC *sec_desc;
74         BOOL ret;
75         NTSTATUS ntstatus;
76         
77         if ( !tdbname ) 
78                 return False;
79         
80         /* get the security descriptor for the file */
81         
82         sec_desc = get_nt_acl_no_snum( info, tdbname );
83         SAFE_FREE( tdbname );
84         
85         if ( !sec_desc ) {
86                 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n", 
87                         tdbname));
88                 return False;
89         }
90         
91         /* run the check, try for the max allowed */
92         
93         ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
94                 &info->access_granted, &ntstatus );
95                 
96         if ( sec_desc )
97                 TALLOC_FREE( sec_desc );
98                 
99         if ( !ret ) {
100                 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
101                         nt_errstr( ntstatus)));
102                 return False;
103         }
104         
105         /* we have to have READ permission for a successful open */
106         
107         return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
108 }
109
110 /********************************************************************
111  ********************************************************************/
112
113 static BOOL elog_validate_logname( const char *name )
114 {
115         int i;
116         const char **elogs = lp_eventlog_list();
117         
118         for ( i=0; elogs[i]; i++ ) {
119                 if ( strequal( name, elogs[i] ) )
120                         return True;
121         }
122         
123         return False;
124 }
125
126 /********************************************************************
127  ********************************************************************/
128
129 static WERROR elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
130 {
131         EVENTLOG_INFO *elog;
132         
133         /* first thing is to validate the eventlog name */
134         
135         if ( !elog_validate_logname( logname ) )
136                 return WERR_OBJECT_PATH_INVALID;
137         
138         if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
139                 return WERR_NOMEM;
140                 
141         elog->logname = talloc_strdup( elog, logname );
142         
143         /* Open the tdb first (so that we can create any new tdbs if necessary).
144            We have to do this as root and then use an internal access check 
145            on the file permissions since you can only have a tdb open once
146            in a single process */
147
148         become_root();
149         elog->tdb = elog_open_tdb( elog->logname );
150         unbecome_root();
151
152         if ( !elog->tdb ) {
153                 /* according to MSDN, if the logfile cannot be found, we should
154                   default to the "Application" log */
155         
156                 if ( !strequal( logname, ELOG_APPL ) ) {
157                 
158                         TALLOC_FREE( elog->logname );
159                         
160                         elog->logname = talloc_strdup( elog, ELOG_APPL );                       
161
162                         /* do the access check */
163                         if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
164                                 TALLOC_FREE( elog );
165                                 return WERR_ACCESS_DENIED;
166                         }
167         
168                         become_root();
169                         elog->tdb = elog_open_tdb( elog->logname );
170                         unbecome_root();
171                 }       
172                 
173                 if ( !elog->tdb ) {
174                         TALLOC_FREE( elog );
175                         return WERR_ACCESS_DENIED;      /* ??? */               
176                 }
177         }
178         
179         /* now do the access check.  Close the tdb if we fail here */
180
181         if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
182                 elog_close_tdb( elog->tdb );
183                 TALLOC_FREE( elog );
184                 return WERR_ACCESS_DENIED;
185         }
186         
187         /* create the policy handle */
188         
189         if ( !create_policy_hnd
190              ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
191                 free_eventlog_info( elog );
192                 return WERR_NOMEM;
193         }
194
195         return WERR_OK;
196 }
197
198 /********************************************************************
199  ********************************************************************/
200
201 static WERROR elog_close( pipes_struct *p, POLICY_HND *hnd )
202 {
203         if ( !( close_policy_hnd( p, hnd ) ) ) {
204                 return WERR_BADFID;
205         }
206
207         return WERR_OK;
208 }
209
210 /*******************************************************************
211  *******************************************************************/
212
213 static int elog_size( EVENTLOG_INFO *info )
214 {
215         if ( !info || !info->tdb ) {
216                 DEBUG(0,("elog_size: Invalid info* structure!\n"));
217                 return 0;
218         }
219
220         return elog_tdb_size( info->tdb, NULL, NULL );
221 }
222
223 /********************************************************************
224   For the given tdb, get the next eventlog record into the passed 
225   Eventlog_entry.  returns NULL if it can't get the record for some reason.
226  ********************************************************************/
227
228 Eventlog_entry *get_eventlog_record( prs_struct * ps, TDB_CONTEXT * tdb,
229                                      int recno, Eventlog_entry * ee )
230 {
231         TDB_DATA ret, key;
232
233         int srecno;
234         int reclen;
235         int len;
236         uint8 *rbuff;
237
238         pstring *wpsource, *wpcomputer, *wpsid, *wpstrs, *puserdata;
239
240         key.dsize = sizeof( int32 );
241         rbuff = NULL;
242
243         srecno = recno;
244         key.dptr = ( char * ) &srecno;
245
246         ret = tdb_fetch( tdb, key );
247
248         if ( ret.dsize == 0 ) {
249                 DEBUG( 8,
250                        ( "Can't find a record for the key, record %d\n",
251                          recno ) );
252                 return NULL;
253         }
254
255         len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
256
257         DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
258
259         if ( !len )
260                 return NULL;
261
262         /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
263
264         if ( !ee )
265                 return NULL;
266
267         len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
268                           &ee->record.length, &ee->record.reserved1,
269                           &ee->record.record_number,
270                           &ee->record.time_generated,
271                           &ee->record.time_written, &ee->record.event_id,
272                           &ee->record.event_type, &ee->record.num_strings,
273                           &ee->record.event_category, &ee->record.reserved2,
274                           &ee->record.closing_record_number,
275                           &ee->record.string_offset,
276                           &ee->record.user_sid_length,
277                           &ee->record.user_sid_offset,
278                           &ee->record.data_length, &ee->record.data_offset,
279                           &ee->data_record.source_name_len, &wpsource,
280                           &ee->data_record.computer_name_len, &wpcomputer,
281                           &ee->data_record.sid_padding,
282                           &ee->record.user_sid_length, &wpsid,
283                           &ee->data_record.strings_len, &wpstrs,
284                           &ee->data_record.user_data_len, &puserdata,
285                           &ee->data_record.data_padding );
286         DEBUG( 10,
287                ( "Read record %d, len in tdb was %d\n",
288                  ee->record.record_number, len ) );
289
290         /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
291            into it's 2nd argment for 'B' */
292
293         if ( wpcomputer )
294                 memcpy( ee->data_record.computer_name, wpcomputer,
295                         ee->data_record.computer_name_len );
296         if ( wpsource )
297                 memcpy( ee->data_record.source_name, wpsource,
298                         ee->data_record.source_name_len );
299
300         if ( wpsid )
301                 memcpy( ee->data_record.sid, wpsid,
302                         ee->record.user_sid_length );
303         if ( wpstrs )
304                 memcpy( ee->data_record.strings, wpstrs,
305                         ee->data_record.strings_len );
306
307         /* note that userdata is a pstring */
308         if ( puserdata )
309                 memcpy( ee->data_record.user_data, puserdata,
310                         ee->data_record.user_data_len );
311
312         SAFE_FREE( wpcomputer );
313         SAFE_FREE( wpsource );
314         SAFE_FREE( wpsid );
315         SAFE_FREE( wpstrs );
316         SAFE_FREE( puserdata );
317
318         DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
319         DEBUG( 10,
320                ( "get_eventlog_record: computer_name %d is ",
321                  ee->data_record.computer_name_len ) );
322         SAFE_FREE( ret.dptr );
323         return ee;
324 }
325
326 /********************************************************************
327  note that this can only be called AFTER the table is constructed, 
328  since it uses the table to find the tdb handle
329  ********************************************************************/
330
331 static BOOL sync_eventlog_params( EVENTLOG_INFO *info )
332 {
333         pstring path;
334         uint32 uiMaxSize;
335         uint32 uiRetention;
336         REGISTRY_KEY *keyinfo;
337         REGISTRY_VALUE *val;
338         REGVAL_CTR *values;
339         WERROR wresult;
340         char *elogname = info->logname;
341
342         DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
343
344         if ( !info->tdb ) {
345                 DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
346                 return False;
347         }
348         /* set resonable defaults.  512Kb on size and 1 week on time */
349
350         uiMaxSize = 0x80000;
351         uiRetention = 604800;
352
353         /* the general idea is to internally open the registry 
354            key and retreive the values.  That way we can continue 
355            to use the same fetch/store api that we use in 
356            srv_reg_nt.c */
357
358         pstr_sprintf( path, "%s/%s", KEY_EVENTLOG, elogname );
359
360         wresult =
361                 regkey_open_internal( &keyinfo, path, get_root_nt_token(  ),
362                                       REG_KEY_READ );
363
364         if ( !W_ERROR_IS_OK( wresult ) ) {
365                 DEBUG( 4,
366                        ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
367                          path, dos_errstr( wresult ) ) );
368                 return False;
369         }
370
371         if ( !( values = TALLOC_ZERO_P( keyinfo, REGVAL_CTR ) ) ) {
372                 TALLOC_FREE( keyinfo );
373                 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
374
375                 return False;
376         }
377         fetch_reg_values( keyinfo, values );
378
379         if ( ( val = regval_ctr_getvalue( values, "Retention" ) ) != NULL )
380                 uiRetention = IVAL( regval_data_p( val ), 0 );
381
382         if ( ( val = regval_ctr_getvalue( values, "MaxSize" ) ) != NULL )
383                 uiMaxSize = IVAL( regval_data_p( val ), 0 );
384
385         regkey_close_internal( keyinfo );
386
387         tdb_store_int32( info->tdb, EVT_MAXSIZE, uiMaxSize );
388         tdb_store_int32( info->tdb, EVT_RETENTION, uiRetention );
389
390         return True;
391 }
392
393 /********************************************************************
394 ********************************************************************/
395
396 static BOOL get_num_records_hook( EVENTLOG_INFO * info )
397 {
398         int next_record;
399         int oldest_record;
400
401         if ( !info->tdb ) {
402                 DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
403                 return False;
404         }
405
406         /* lock the tdb since we have to get 2 records */
407
408         tdb_lock_bystring( info->tdb, EVT_NEXT_RECORD, 1 );
409         next_record = tdb_fetch_int32( info->tdb, EVT_NEXT_RECORD);
410         oldest_record = tdb_fetch_int32( info->tdb, EVT_OLDEST_ENTRY);
411         tdb_unlock_bystring( info->tdb, EVT_NEXT_RECORD);
412
413         DEBUG( 8,
414                ( "Oldest Record %d; Next Record %d\n", oldest_record,
415                  next_record ) );
416
417         info->num_records = ( next_record - oldest_record );
418         info->oldest_entry = oldest_record;
419
420         return True;
421 }
422
423 /********************************************************************
424  ********************************************************************/
425
426 static BOOL get_oldest_entry_hook( EVENTLOG_INFO * info )
427 {
428
429         /* it's the same thing */
430         return get_num_records_hook( info );
431 }
432
433 /********************************************************************
434  ********************************************************************/
435
436 static Eventlog_entry *read_package_entry( prs_struct * ps,
437                                            EVENTLOG_Q_READ_EVENTLOG * q_u,
438                                            EVENTLOG_R_READ_EVENTLOG * r_u,
439                                            Eventlog_entry * entry )
440 {
441         uint8 *offset;
442         Eventlog_entry *ee_new = NULL;
443
444         ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
445         if ( ee_new == NULL ) {
446                 return NULL;
447         }
448
449         entry->data_record.sid_padding =
450                 ( ( 4 -
451                     ( ( entry->data_record.source_name_len +
452                         entry->data_record.computer_name_len ) % 4 ) ) % 4 );
453         entry->data_record.data_padding =
454                 ( 4 -
455                   ( ( entry->data_record.strings_len +
456                       entry->data_record.user_data_len ) % 4 ) ) % 4;
457         entry->record.length = sizeof( Eventlog_record );
458         entry->record.length += entry->data_record.source_name_len;
459         entry->record.length += entry->data_record.computer_name_len;
460         if ( entry->record.user_sid_length == 0 ) {
461                 /* Should not pad to a DWORD boundary for writing out the sid if there is
462                    no SID, so just propagate the padding to pad the data */
463                 entry->data_record.data_padding +=
464                         entry->data_record.sid_padding;
465                 entry->data_record.sid_padding = 0;
466         }
467         DEBUG( 10,
468                ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
469         DEBUG( 10,
470                ( "data_padding is [%d].\n",
471                  entry->data_record.data_padding ) );
472
473         entry->record.length += entry->data_record.sid_padding;
474         entry->record.length += entry->record.user_sid_length;
475         entry->record.length += entry->data_record.strings_len;
476         entry->record.length += entry->data_record.user_data_len;
477         entry->record.length += entry->data_record.data_padding;
478         /* need another copy of length at the end of the data */
479         entry->record.length += sizeof( entry->record.length );
480         DEBUG( 10,
481                ( "entry->record.length is [%d].\n", entry->record.length ) );
482         entry->data =
483                 PRS_ALLOC_MEM( ps, uint8,
484                                entry->record.length -
485                                sizeof( Eventlog_record ) -
486                                sizeof( entry->record.length ) );
487         if ( entry->data == NULL ) {
488                 return NULL;
489         }
490         offset = entry->data;
491         memcpy( offset, &( entry->data_record.source_name ),
492                 entry->data_record.source_name_len );
493         offset += entry->data_record.source_name_len;
494         memcpy( offset, &( entry->data_record.computer_name ),
495                 entry->data_record.computer_name_len );
496         offset += entry->data_record.computer_name_len;
497         /* SID needs to be DWORD-aligned */
498         offset += entry->data_record.sid_padding;
499         entry->record.user_sid_offset =
500                 sizeof( Eventlog_record ) + ( offset - entry->data );
501         memcpy( offset, &( entry->data_record.sid ),
502                 entry->record.user_sid_length );
503         offset += entry->record.user_sid_length;
504         /* Now do the strings */
505         entry->record.string_offset =
506                 sizeof( Eventlog_record ) + ( offset - entry->data );
507         memcpy( offset, &( entry->data_record.strings ),
508                 entry->data_record.strings_len );
509         offset += entry->data_record.strings_len;
510         /* Now do the data */
511         entry->record.data_length = entry->data_record.user_data_len;
512         entry->record.data_offset =
513                 sizeof( Eventlog_record ) + ( offset - entry->data );
514         memcpy( offset, &( entry->data_record.user_data ),
515                 entry->data_record.user_data_len );
516         offset += entry->data_record.user_data_len;
517
518         memcpy( &( ee_new->record ), &entry->record,
519                 sizeof( Eventlog_record ) );
520         memcpy( &( ee_new->data_record ), &entry->data_record,
521                 sizeof( Eventlog_data_record ) );
522         ee_new->data = entry->data;
523
524         return ee_new;
525 }
526
527 /********************************************************************
528  ********************************************************************/
529
530 static BOOL add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
531                                 Eventlog_entry * ee_new )
532 {
533         Eventlog_entry *insert_point;
534
535         insert_point = r_u->entry;
536
537         if ( NULL == insert_point ) {
538                 r_u->entry = ee_new;
539                 ee_new->next = NULL;
540         } else {
541                 while ( ( NULL != insert_point->next ) ) {
542                         insert_point = insert_point->next;
543                 }
544                 ee_new->next = NULL;
545                 insert_point->next = ee_new;
546         }
547         r_u->num_records++;
548         r_u->num_bytes_in_resp += ee_new->record.length;
549
550         return True;
551 }
552
553 /********************************************************************
554  ********************************************************************/
555
556 WERROR _eventlog_open_eventlog( pipes_struct * p,
557                                 EVENTLOG_Q_OPEN_EVENTLOG * q_u,
558                                 EVENTLOG_R_OPEN_EVENTLOG * r_u )
559 {
560         fstring servername, logname;
561         EVENTLOG_INFO *info;
562         WERROR wresult;
563
564         fstrcpy( servername, "" );
565         if ( q_u->servername.string ) {
566                 rpcstr_pull( servername, q_u->servername.string->buffer,
567                              sizeof( servername ),
568                              q_u->servername.string->uni_str_len * 2, 0 );
569         }
570
571         fstrcpy( logname, "" );
572         if ( q_u->logname.string ) {
573                 rpcstr_pull( logname, q_u->logname.string->buffer,
574                              sizeof( logname ),
575                              q_u->logname.string->uni_str_len * 2, 0 );
576         }
577         
578         DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
579                 servername, logname ));
580                 
581         /* according to MSDN, if the logfile cannot be found, we should
582           default to the "Application" log */
583           
584         if ( !W_ERROR_IS_OK( wresult = elog_open( p, logname, &r_u->handle )) )
585                 return wresult;
586
587         if ( !(info = find_eventlog_info_by_hnd( p, &r_u->handle )) ) {
588                 DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
589                         logname ));
590                 elog_close( p, &r_u->handle );
591                 return WERR_BADFID;
592         }
593
594         DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
595
596         sync_eventlog_params( info );
597         prune_eventlog( info->tdb );
598
599         return WERR_OK;
600 }
601
602 /********************************************************************
603  This call still needs some work
604  ********************************************************************/
605
606 WERROR _eventlog_clear_eventlog( pipes_struct * p,
607                                  EVENTLOG_Q_CLEAR_EVENTLOG * q_u,
608                                  EVENTLOG_R_CLEAR_EVENTLOG * r_u )
609 {
610         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
611         pstring backup_file_name;
612
613         if ( !info )
614                 return WERR_BADFID;
615
616         pstrcpy( backup_file_name, "" );
617         if ( q_u->backupfile.string ) {
618                 rpcstr_pull( backup_file_name, q_u->backupfile.string->buffer,
619                              sizeof( backup_file_name ),
620                              q_u->backupfile.string->uni_str_len * 2, 0 );
621         }
622
623         DEBUG( 8,
624                ( "_eventlog_clear_eventlog: Using [%s] as the backup file name for log [%s].",
625                  backup_file_name, info->logname ) );
626
627 #if 0 
628         /* close the current one, reinit */
629
630         tdb_close( info->tdb ); 
631
632         if ( !(info->tdb = elog_init_tdb( ttdb[i].tdbfname )) )
633                 return WERR_ACCESS_DENIED;
634 #endif
635
636         return WERR_OK;
637 }
638
639 /********************************************************************
640  ********************************************************************/
641
642 WERROR _eventlog_close_eventlog( pipes_struct * p,
643                                  EVENTLOG_Q_CLOSE_EVENTLOG * q_u,
644                                  EVENTLOG_R_CLOSE_EVENTLOG * r_u )
645 {
646         return elog_close( p, &q_u->handle );
647 }
648
649 /********************************************************************
650  ********************************************************************/
651
652 WERROR _eventlog_read_eventlog( pipes_struct * p,
653                                 EVENTLOG_Q_READ_EVENTLOG * q_u,
654                                 EVENTLOG_R_READ_EVENTLOG * r_u )
655 {
656         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
657         Eventlog_entry entry, *ee_new;
658
659         uint32 num_records_read = 0;
660         prs_struct *ps;
661         int bytes_left, record_number;
662         TDB_CONTEXT *tdb;
663
664         info->flags = q_u->flags;
665         ps = &p->out_data.rdata;
666
667         bytes_left = q_u->max_read_size;
668         tdb = info->tdb;
669         if ( !tdb ) {
670                 return WERR_EVENTLOG_FILE_CORRUPT;
671         }
672
673         /* DEBUG(8,("Bytes left is %d\n",bytes_left)); */
674
675         record_number = q_u->offset;
676
677         while ( bytes_left > 0 ) {
678                 if ( get_eventlog_record
679                      ( ps, tdb, record_number, &entry ) ) {
680                         DEBUG( 8,
681                                ( "Retrieved record %d\n", record_number ) );
682                                
683                         /* Now see if there is enough room to add */
684                         ee_new = read_package_entry( ps, q_u, r_u,&entry );
685                         if ( !ee_new )
686                                 return WERR_NOMEM;
687
688                         if ( r_u->num_bytes_in_resp + ee_new->record.length >
689                              q_u->max_read_size ) {
690                                 r_u->bytes_in_next_record =
691                                         ee_new->record.length;
692                                         
693                                 /* response would be too big to fit in client-size buffer */
694                                 
695                                 bytes_left = 0;
696                                 break;
697                         }
698                         
699                         add_record_to_resp( r_u, ee_new );
700                         bytes_left -= ee_new->record.length;
701                         ZERO_STRUCT( entry );
702                         num_records_read =
703                                 r_u->num_records - num_records_read;
704                                 
705                         DEBUG( 10,
706                                ( "_eventlog_read_eventlog: read [%d] records for a total of [%d] records using [%d] bytes out of a max of [%d].\n",
707                                  num_records_read, r_u->num_records,
708                                  r_u->num_bytes_in_resp,
709                                  q_u->max_read_size ) );
710                 } else {
711                         DEBUG( 8, ( "get_eventlog_record returned NULL\n" ) );
712                         return WERR_NOMEM;      /* wrong error - but return one anyway */
713                 }
714
715
716                 if ( info->flags & EVENTLOG_FORWARDS_READ )
717                         record_number++;
718                 else
719                         record_number--;
720         }
721         
722         return WERR_OK;
723 }
724
725 /********************************************************************
726  ********************************************************************/
727
728 WERROR _eventlog_get_oldest_entry( pipes_struct * p,
729                                    EVENTLOG_Q_GET_OLDEST_ENTRY * q_u,
730                                    EVENTLOG_R_GET_OLDEST_ENTRY * r_u )
731 {
732         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
733
734         if ( !( get_oldest_entry_hook( info ) ) )
735                 return WERR_BADFILE;
736
737         r_u->oldest_entry = info->oldest_entry;
738
739         return WERR_OK;
740 }
741
742 /********************************************************************
743  ********************************************************************/
744
745 WERROR _eventlog_get_num_records( pipes_struct * p,
746                                   EVENTLOG_Q_GET_NUM_RECORDS * q_u,
747                                   EVENTLOG_R_GET_NUM_RECORDS * r_u )
748 {
749         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
750
751         if ( !( get_num_records_hook( info ) ) )
752                 return WERR_BADFILE;
753
754         r_u->num_records = info->num_records;
755
756         return WERR_OK;
757 }