r11332: eventlog API uses NTSTATUS, not WERROR for return codes
[Samba.git] / source / rpc_server / srv_eventlog_nt.c
blob6413221031cf3ee808fa95fa5a3e4fa32577f807
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.
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.
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.
23 #include "includes.h"
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
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;
37 /********************************************************************
38 ********************************************************************/
40 static void free_eventlog_info( void *ptr )
42 EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
44 if ( elog->tdb )
45 elog_close_tdb( elog->tdb );
47 TALLOC_FREE( elog );
50 /********************************************************************
51 ********************************************************************/
53 static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
54 POLICY_HND * handle )
56 EVENTLOG_INFO *info;
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;
64 return info;
67 /********************************************************************
68 ********************************************************************/
70 static BOOL elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
72 char *tdbname = elog_tdbname( info->logname );
73 SEC_DESC *sec_desc;
74 BOOL ret;
75 NTSTATUS ntstatus;
77 if ( !tdbname )
78 return False;
80 /* get the security descriptor for the file */
82 sec_desc = get_nt_acl_no_snum( info, tdbname );
83 SAFE_FREE( tdbname );
85 if ( !sec_desc ) {
86 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
87 tdbname));
88 return False;
91 /* root free pass */
93 if ( geteuid() == sec_initial_uid() ) {
94 DEBUG(5,("elog_check_access: using root's token\n"));
95 token = get_root_nt_token();
98 /* run the check, try for the max allowed */
100 ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
101 &info->access_granted, &ntstatus );
103 if ( sec_desc )
104 TALLOC_FREE( sec_desc );
106 if ( !ret ) {
107 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
108 nt_errstr( ntstatus)));
109 return False;
112 /* we have to have READ permission for a successful open */
114 return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
117 /********************************************************************
118 ********************************************************************/
120 static BOOL elog_validate_logname( const char *name )
122 int i;
123 const char **elogs = lp_eventlog_list();
125 for ( i=0; elogs[i]; i++ ) {
126 if ( strequal( name, elogs[i] ) )
127 return True;
130 return False;
133 /********************************************************************
134 ********************************************************************/
136 static NTSTATUS elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
138 EVENTLOG_INFO *elog;
140 /* first thing is to validate the eventlog name */
142 if ( !elog_validate_logname( logname ) )
143 return NT_STATUS_OBJECT_PATH_INVALID;
145 if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
146 return NT_STATUS_NO_MEMORY;
148 elog->logname = talloc_strdup( elog, logname );
150 /* Open the tdb first (so that we can create any new tdbs if necessary).
151 We have to do this as root and then use an internal access check
152 on the file permissions since you can only have a tdb open once
153 in a single process */
155 become_root();
156 elog->tdb = elog_open_tdb( elog->logname );
157 unbecome_root();
159 if ( !elog->tdb ) {
160 /* according to MSDN, if the logfile cannot be found, we should
161 default to the "Application" log */
163 if ( !strequal( logname, ELOG_APPL ) ) {
165 TALLOC_FREE( elog->logname );
167 elog->logname = talloc_strdup( elog, ELOG_APPL );
169 /* do the access check */
170 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
171 TALLOC_FREE( elog );
172 return NT_STATUS_ACCESS_DENIED;
175 become_root();
176 elog->tdb = elog_open_tdb( elog->logname );
177 unbecome_root();
180 if ( !elog->tdb ) {
181 TALLOC_FREE( elog );
182 return NT_STATUS_ACCESS_DENIED; /* ??? */
186 /* now do the access check. Close the tdb if we fail here */
188 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
189 elog_close_tdb( elog->tdb );
190 TALLOC_FREE( elog );
191 return NT_STATUS_ACCESS_DENIED;
194 /* create the policy handle */
196 if ( !create_policy_hnd
197 ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
198 free_eventlog_info( elog );
199 return NT_STATUS_NO_MEMORY;
202 return NT_STATUS_OK;
205 /********************************************************************
206 ********************************************************************/
208 static NTSTATUS elog_close( pipes_struct *p, POLICY_HND *hnd )
210 if ( !( close_policy_hnd( p, hnd ) ) ) {
211 return NT_STATUS_INVALID_HANDLE;
214 return NT_STATUS_OK;
217 /*******************************************************************
218 *******************************************************************/
220 static int elog_size( EVENTLOG_INFO *info )
222 if ( !info || !info->tdb ) {
223 DEBUG(0,("elog_size: Invalid info* structure!\n"));
224 return 0;
227 return elog_tdb_size( info->tdb, NULL, NULL );
230 /********************************************************************
231 For the given tdb, get the next eventlog record into the passed
232 Eventlog_entry. returns NULL if it can't get the record for some reason.
233 ********************************************************************/
235 Eventlog_entry *get_eventlog_record( prs_struct * ps, TDB_CONTEXT * tdb,
236 int recno, Eventlog_entry * ee )
238 TDB_DATA ret, key;
240 int srecno;
241 int reclen;
242 int len;
243 uint8 *rbuff;
245 pstring *wpsource, *wpcomputer, *wpsid, *wpstrs, *puserdata;
247 key.dsize = sizeof( int32 );
248 rbuff = NULL;
250 srecno = recno;
251 key.dptr = ( char * ) &srecno;
253 ret = tdb_fetch( tdb, key );
255 if ( ret.dsize == 0 ) {
256 DEBUG( 8,
257 ( "Can't find a record for the key, record %d\n",
258 recno ) );
259 return NULL;
262 len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
264 DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
266 if ( !len )
267 return NULL;
269 /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
271 if ( !ee )
272 return NULL;
274 len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
275 &ee->record.length, &ee->record.reserved1,
276 &ee->record.record_number,
277 &ee->record.time_generated,
278 &ee->record.time_written, &ee->record.event_id,
279 &ee->record.event_type, &ee->record.num_strings,
280 &ee->record.event_category, &ee->record.reserved2,
281 &ee->record.closing_record_number,
282 &ee->record.string_offset,
283 &ee->record.user_sid_length,
284 &ee->record.user_sid_offset,
285 &ee->record.data_length, &ee->record.data_offset,
286 &ee->data_record.source_name_len, &wpsource,
287 &ee->data_record.computer_name_len, &wpcomputer,
288 &ee->data_record.sid_padding,
289 &ee->record.user_sid_length, &wpsid,
290 &ee->data_record.strings_len, &wpstrs,
291 &ee->data_record.user_data_len, &puserdata,
292 &ee->data_record.data_padding );
293 DEBUG( 10,
294 ( "Read record %d, len in tdb was %d\n",
295 ee->record.record_number, len ) );
297 /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
298 into it's 2nd argment for 'B' */
300 if ( wpcomputer )
301 memcpy( ee->data_record.computer_name, wpcomputer,
302 ee->data_record.computer_name_len );
303 if ( wpsource )
304 memcpy( ee->data_record.source_name, wpsource,
305 ee->data_record.source_name_len );
307 if ( wpsid )
308 memcpy( ee->data_record.sid, wpsid,
309 ee->record.user_sid_length );
310 if ( wpstrs )
311 memcpy( ee->data_record.strings, wpstrs,
312 ee->data_record.strings_len );
314 /* note that userdata is a pstring */
315 if ( puserdata )
316 memcpy( ee->data_record.user_data, puserdata,
317 ee->data_record.user_data_len );
319 SAFE_FREE( wpcomputer );
320 SAFE_FREE( wpsource );
321 SAFE_FREE( wpsid );
322 SAFE_FREE( wpstrs );
323 SAFE_FREE( puserdata );
325 DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
326 DEBUG( 10,
327 ( "get_eventlog_record: computer_name %d is ",
328 ee->data_record.computer_name_len ) );
329 SAFE_FREE( ret.dptr );
330 return ee;
333 /********************************************************************
334 note that this can only be called AFTER the table is constructed,
335 since it uses the table to find the tdb handle
336 ********************************************************************/
338 static BOOL sync_eventlog_params( EVENTLOG_INFO *info )
340 pstring path;
341 uint32 uiMaxSize;
342 uint32 uiRetention;
343 REGISTRY_KEY *keyinfo;
344 REGISTRY_VALUE *val;
345 REGVAL_CTR *values;
346 WERROR wresult;
347 char *elogname = info->logname;
349 DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
351 if ( !info->tdb ) {
352 DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
353 return False;
355 /* set resonable defaults. 512Kb on size and 1 week on time */
357 uiMaxSize = 0x80000;
358 uiRetention = 604800;
360 /* the general idea is to internally open the registry
361 key and retreive the values. That way we can continue
362 to use the same fetch/store api that we use in
363 srv_reg_nt.c */
365 pstr_sprintf( path, "%s/%s", KEY_EVENTLOG, elogname );
367 wresult =
368 regkey_open_internal( &keyinfo, path, get_root_nt_token( ),
369 REG_KEY_READ );
371 if ( !W_ERROR_IS_OK( wresult ) ) {
372 DEBUG( 4,
373 ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
374 path, dos_errstr( wresult ) ) );
375 return False;
378 if ( !( values = TALLOC_ZERO_P( keyinfo, REGVAL_CTR ) ) ) {
379 TALLOC_FREE( keyinfo );
380 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
382 return False;
384 fetch_reg_values( keyinfo, values );
386 if ( ( val = regval_ctr_getvalue( values, "Retention" ) ) != NULL )
387 uiRetention = IVAL( regval_data_p( val ), 0 );
389 if ( ( val = regval_ctr_getvalue( values, "MaxSize" ) ) != NULL )
390 uiMaxSize = IVAL( regval_data_p( val ), 0 );
392 regkey_close_internal( keyinfo );
394 tdb_store_int32( info->tdb, EVT_MAXSIZE, uiMaxSize );
395 tdb_store_int32( info->tdb, EVT_RETENTION, uiRetention );
397 return True;
400 /********************************************************************
401 ********************************************************************/
403 static BOOL get_num_records_hook( EVENTLOG_INFO * info )
405 int next_record;
406 int oldest_record;
408 if ( !info->tdb ) {
409 DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
410 return False;
413 /* lock the tdb since we have to get 2 records */
415 tdb_lock_bystring( info->tdb, EVT_NEXT_RECORD, 1 );
416 next_record = tdb_fetch_int32( info->tdb, EVT_NEXT_RECORD);
417 oldest_record = tdb_fetch_int32( info->tdb, EVT_OLDEST_ENTRY);
418 tdb_unlock_bystring( info->tdb, EVT_NEXT_RECORD);
420 DEBUG( 8,
421 ( "Oldest Record %d; Next Record %d\n", oldest_record,
422 next_record ) );
424 info->num_records = ( next_record - oldest_record );
425 info->oldest_entry = oldest_record;
427 return True;
430 /********************************************************************
431 ********************************************************************/
433 static BOOL get_oldest_entry_hook( EVENTLOG_INFO * info )
435 /* it's the same thing */
436 return get_num_records_hook( info );
439 /********************************************************************
440 ********************************************************************/
442 static Eventlog_entry *read_package_entry( prs_struct * ps,
443 EVENTLOG_Q_READ_EVENTLOG * q_u,
444 EVENTLOG_R_READ_EVENTLOG * r_u,
445 Eventlog_entry * entry )
447 uint8 *offset;
448 Eventlog_entry *ee_new = NULL;
450 ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
451 if ( ee_new == NULL ) {
452 return NULL;
455 entry->data_record.sid_padding =
456 ( ( 4 -
457 ( ( entry->data_record.source_name_len +
458 entry->data_record.computer_name_len ) % 4 ) ) % 4 );
459 entry->data_record.data_padding =
460 ( 4 -
461 ( ( entry->data_record.strings_len +
462 entry->data_record.user_data_len ) % 4 ) ) % 4;
463 entry->record.length = sizeof( Eventlog_record );
464 entry->record.length += entry->data_record.source_name_len;
465 entry->record.length += entry->data_record.computer_name_len;
466 if ( entry->record.user_sid_length == 0 ) {
467 /* Should not pad to a DWORD boundary for writing out the sid if there is
468 no SID, so just propagate the padding to pad the data */
469 entry->data_record.data_padding +=
470 entry->data_record.sid_padding;
471 entry->data_record.sid_padding = 0;
473 DEBUG( 10,
474 ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
475 DEBUG( 10,
476 ( "data_padding is [%d].\n",
477 entry->data_record.data_padding ) );
479 entry->record.length += entry->data_record.sid_padding;
480 entry->record.length += entry->record.user_sid_length;
481 entry->record.length += entry->data_record.strings_len;
482 entry->record.length += entry->data_record.user_data_len;
483 entry->record.length += entry->data_record.data_padding;
484 /* need another copy of length at the end of the data */
485 entry->record.length += sizeof( entry->record.length );
486 DEBUG( 10,
487 ( "entry->record.length is [%d].\n", entry->record.length ) );
488 entry->data =
489 PRS_ALLOC_MEM( ps, uint8,
490 entry->record.length -
491 sizeof( Eventlog_record ) -
492 sizeof( entry->record.length ) );
493 if ( entry->data == NULL ) {
494 return NULL;
496 offset = entry->data;
497 memcpy( offset, &( entry->data_record.source_name ),
498 entry->data_record.source_name_len );
499 offset += entry->data_record.source_name_len;
500 memcpy( offset, &( entry->data_record.computer_name ),
501 entry->data_record.computer_name_len );
502 offset += entry->data_record.computer_name_len;
503 /* SID needs to be DWORD-aligned */
504 offset += entry->data_record.sid_padding;
505 entry->record.user_sid_offset =
506 sizeof( Eventlog_record ) + ( offset - entry->data );
507 memcpy( offset, &( entry->data_record.sid ),
508 entry->record.user_sid_length );
509 offset += entry->record.user_sid_length;
510 /* Now do the strings */
511 entry->record.string_offset =
512 sizeof( Eventlog_record ) + ( offset - entry->data );
513 memcpy( offset, &( entry->data_record.strings ),
514 entry->data_record.strings_len );
515 offset += entry->data_record.strings_len;
516 /* Now do the data */
517 entry->record.data_length = entry->data_record.user_data_len;
518 entry->record.data_offset =
519 sizeof( Eventlog_record ) + ( offset - entry->data );
520 memcpy( offset, &( entry->data_record.user_data ),
521 entry->data_record.user_data_len );
522 offset += entry->data_record.user_data_len;
524 memcpy( &( ee_new->record ), &entry->record,
525 sizeof( Eventlog_record ) );
526 memcpy( &( ee_new->data_record ), &entry->data_record,
527 sizeof( Eventlog_data_record ) );
528 ee_new->data = entry->data;
530 return ee_new;
533 /********************************************************************
534 ********************************************************************/
536 static BOOL add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
537 Eventlog_entry * ee_new )
539 Eventlog_entry *insert_point;
541 insert_point = r_u->entry;
543 if ( NULL == insert_point ) {
544 r_u->entry = ee_new;
545 ee_new->next = NULL;
546 } else {
547 while ( ( NULL != insert_point->next ) ) {
548 insert_point = insert_point->next;
550 ee_new->next = NULL;
551 insert_point->next = ee_new;
553 r_u->num_records++;
554 r_u->num_bytes_in_resp += ee_new->record.length;
556 return True;
559 /********************************************************************
560 ********************************************************************/
562 NTSTATUS _eventlog_open_eventlog( pipes_struct * p,
563 EVENTLOG_Q_OPEN_EVENTLOG * q_u,
564 EVENTLOG_R_OPEN_EVENTLOG * r_u )
566 fstring servername, logname;
567 EVENTLOG_INFO *info;
568 NTSTATUS result;
570 fstrcpy( servername, "" );
571 if ( q_u->servername.string ) {
572 rpcstr_pull( servername, q_u->servername.string->buffer,
573 sizeof( servername ),
574 q_u->servername.string->uni_str_len * 2, 0 );
577 fstrcpy( logname, "" );
578 if ( q_u->logname.string ) {
579 rpcstr_pull( logname, q_u->logname.string->buffer,
580 sizeof( logname ),
581 q_u->logname.string->uni_str_len * 2, 0 );
584 DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
585 servername, logname ));
587 /* according to MSDN, if the logfile cannot be found, we should
588 default to the "Application" log */
590 if ( !NT_STATUS_IS_OK( result = elog_open( p, logname, &r_u->handle )) )
591 return result;
593 if ( !(info = find_eventlog_info_by_hnd( p, &r_u->handle )) ) {
594 DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
595 logname ));
596 elog_close( p, &r_u->handle );
597 return NT_STATUS_INVALID_HANDLE;
600 DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
602 sync_eventlog_params( info );
603 prune_eventlog( info->tdb );
605 return NT_STATUS_OK;
608 /********************************************************************
609 This call still needs some work
610 ********************************************************************/
612 NTSTATUS _eventlog_clear_eventlog( pipes_struct * p,
613 EVENTLOG_Q_CLEAR_EVENTLOG * q_u,
614 EVENTLOG_R_CLEAR_EVENTLOG * r_u )
616 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
617 pstring backup_file_name;
619 if ( !info )
620 return NT_STATUS_INVALID_HANDLE;
622 pstrcpy( backup_file_name, "" );
623 if ( q_u->backupfile.string ) {
624 rpcstr_pull( backup_file_name, q_u->backupfile.string->buffer,
625 sizeof( backup_file_name ),
626 q_u->backupfile.string->uni_str_len * 2, 0 );
629 DEBUG( 8,
630 ( "_eventlog_clear_eventlog: Using [%s] as the backup file name for log [%s].",
631 backup_file_name, info->logname ) );
633 #if 0
634 /* close the current one, reinit */
636 tdb_close( info->tdb );
638 if ( !(info->tdb = elog_init_tdb( ttdb[i].tdbfname )) )
639 return NT_STATUS_ACCESS_DENIED;
640 #endif
642 return NT_STATUS_OK;
645 /********************************************************************
646 ********************************************************************/
648 NTSTATUS _eventlog_close_eventlog( pipes_struct * p,
649 EVENTLOG_Q_CLOSE_EVENTLOG * q_u,
650 EVENTLOG_R_CLOSE_EVENTLOG * r_u )
652 return elog_close( p, &q_u->handle );
655 /********************************************************************
656 ********************************************************************/
658 NTSTATUS _eventlog_read_eventlog( pipes_struct * p,
659 EVENTLOG_Q_READ_EVENTLOG * q_u,
660 EVENTLOG_R_READ_EVENTLOG * r_u )
662 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
663 Eventlog_entry entry, *ee_new;
665 uint32 num_records_read = 0;
666 prs_struct *ps;
667 int bytes_left, record_number;
668 TDB_CONTEXT *tdb;
670 info->flags = q_u->flags;
671 ps = &p->out_data.rdata;
673 bytes_left = q_u->max_read_size;
674 tdb = info->tdb;
675 if ( !tdb ) {
676 return NT_STATUS_ACCESS_DENIED;
679 /* DEBUG(8,("Bytes left is %d\n",bytes_left)); */
681 record_number = q_u->offset;
683 while ( bytes_left > 0 ) {
684 if ( get_eventlog_record
685 ( ps, tdb, record_number, &entry ) ) {
686 DEBUG( 8,
687 ( "Retrieved record %d\n", record_number ) );
689 /* Now see if there is enough room to add */
690 ee_new = read_package_entry( ps, q_u, r_u,&entry );
691 if ( !ee_new )
692 return NT_STATUS_NO_MEMORY;
694 if ( r_u->num_bytes_in_resp + ee_new->record.length >
695 q_u->max_read_size ) {
696 r_u->bytes_in_next_record =
697 ee_new->record.length;
699 /* response would be too big to fit in client-size buffer */
701 bytes_left = 0;
702 break;
705 add_record_to_resp( r_u, ee_new );
706 bytes_left -= ee_new->record.length;
707 ZERO_STRUCT( entry );
708 num_records_read =
709 r_u->num_records - num_records_read;
711 DEBUG( 10,
712 ( "_eventlog_read_eventlog: read [%d] records for a total of [%d] records using [%d] bytes out of a max of [%d].\n",
713 num_records_read, r_u->num_records,
714 r_u->num_bytes_in_resp,
715 q_u->max_read_size ) );
716 } else {
717 DEBUG( 8, ( "get_eventlog_record returned NULL\n" ) );
718 return NT_STATUS_NO_MEMORY; /* wrong error - but return one anyway */
722 if ( info->flags & EVENTLOG_FORWARDS_READ )
723 record_number++;
724 else
725 record_number--;
728 return NT_STATUS_OK;
731 /********************************************************************
732 ********************************************************************/
734 NTSTATUS _eventlog_get_oldest_entry( pipes_struct * p,
735 EVENTLOG_Q_GET_OLDEST_ENTRY * q_u,
736 EVENTLOG_R_GET_OLDEST_ENTRY * r_u )
738 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
740 if ( !( get_oldest_entry_hook( info ) ) )
741 return NT_STATUS_ACCESS_DENIED;
743 r_u->oldest_entry = info->oldest_entry;
745 return NT_STATUS_OK;
748 /********************************************************************
749 ********************************************************************/
751 NTSTATUS _eventlog_get_num_records( pipes_struct * p,
752 EVENTLOG_Q_GET_NUM_RECORDS * q_u,
753 EVENTLOG_R_GET_NUM_RECORDS * r_u )
755 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
757 if ( !( get_num_records_hook( info ) ) )
758 return NT_STATUS_ACCESS_DENIED;
760 r_u->num_records = info->num_records;
762 return NT_STATUS_OK;