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