\n";
17 | probes_info_form_tabel($probes, $probeid);
18 |
19 | # Trick incl the graph php script
20 | #include("../include/graph_probe_drops_bar01.php");
21 | include("../include/graph_probe_drops_bar02.php");
22 |
23 | include("../staging/pie01.php");
24 |
25 | db_disconnect();
26 |
27 | doFooter();
28 | displayTimingInfo($starttime, getMicroTime(), $displayTiming, "php done");
29 | ?>
30 |
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 |
2 | == Before release ==
3 |
4 | Stuff I/we need to change before a release
5 |
6 | * Currently no show stoppers for a release.
7 |
8 |
9 | == Future ==
10 |
11 | Use of JSON encoding in proc file? I foresee/predict that we need to
12 | relay more advanced information via the kernel proc file, to
13 | userspace. The easiest way would be to encode the proc file string as
14 | a JSON string.
15 |
16 | More advanced info would be need, when starting to implement/record
17 | packet timestamping, and the PCR clock signals. Each stream can have
18 | different threshold levels, regarding averate inter-packet arrival
19 | time (IAT) and "allowed" bursts, dependend on the bandwidth and the
20 | PCR clock. E.g. for a high-bandwidth HD channel is normal to see IAT
21 | down to 0.3 ms, while for a low-bandwidth channel this would be
22 | considered a burst.
23 |
24 |
25 | == iptables-module ==
26 |
27 | Implement byte and packet counters, for no-signal detection.
28 |
29 |
30 | == Database ==
31 |
32 | Document the tables, right now there is only the notes I made during
33 | the evolution of the code.
34 |
35 |
36 | == Collector ==
37 |
38 | Implement no-signal detection, based on the byte and packet counters
39 | from the kernel module.
40 |
41 | == Homepage ==
42 |
43 | == Ideas ==
44 |
45 | To measure the collective burstiness behavior for all of the streams /
46 | the entire network-stack, we could "just" look at the number of
47 | packets being processed in NAPI mode.
48 |
49 |
--------------------------------------------------------------------------------
/README.wiki:
--------------------------------------------------------------------------------
1 |
2 | = IPTV-Analyzer =
3 |
4 | The IPTV-Analyzer is a continuous/real-time tool for analyzing the
5 | contents of MPEG2 Transport Stream (TS) packets, which is commonly
6 | used for IPTV multicast signals.
7 |
8 | The main purpose is continuous quality measurement, with a focus on
9 | detecting MPEG2 TS/CC packet drops.
10 |
11 | It scales to hundreds of IPTV channels, even on small ATOM based CPUs.
12 |
13 | == Private vs. Enterprise users ==
14 |
15 | Private users can also take advantage of this software, as you can now
16 | document IPTV problems towards your provider, without having to buy
17 | commercial analyzer tools that are way to expensive to home users.
18 |
19 | Enterprise users can save a lot of money, as alternative commercial
20 | analyzer tools are extremely expensive. The IPTV-Analyzer is mostly
21 | target for enterprise usage, with several probes/measurement-points at
22 | strategic points in your network.
23 |
24 | == Elements / Architecture ==
25 |
26 | === iptables-module: mpeg2ts ===
27 |
28 | The core component is an iptables (Linux) kernel module, named
29 | "mpeg2ts". This kernel module performs the real-time deep packet
30 | inspection of the MPEG2-TS packets. Its highly performance optimized,
31 | written for parallel processing across CPU cores (via RCU locking) and
32 | hash tables are used for handling large number of streams. Statistics
33 | are exported via the proc filesystem (scalability is achieved via use
34 | of the seq_file proc API).
35 |
36 | === Collector ===
37 |
38 | The statistics are collected by a iptv-collector daemon, and stored in
39 | a MySQL database.
40 |
41 | == Price ==
42 |
43 | If you agree to contribute back fixes, improvements, bug-reports,
44 | insults etc., then its FREE, as its Licensed under GPL.
45 |
--------------------------------------------------------------------------------
/Makefile.am:
--------------------------------------------------------------------------------
1 | # -*- Makefile -*-
2 | #
3 | # @configure_input@
4 | #
5 | # This file is NOT autogenerated, and need to be maintained. Automake
6 | # will generate the Makefile based on this file where all the
7 | # appropiate variables are defined inside.
8 | #
9 | #
10 | ACLOCAL_AMFLAGS = -I m4
11 |
12 | # Adding to SUBDIRS will automatically call/invoke the Makefile in
13 | # these dirs
14 | SUBDIRS = iptables-module collector
15 |
16 | #man_MANS := xtables-addons.8
17 |
18 | .PHONY: FORCE
19 | FORCE:
20 |
21 | #xtables-addons.8: FORCE
22 | # ${MAKE} -f Makefile.mans all;
23 |
24 | install-exec-hook:
25 | depmod -a || :;
26 |
27 | #config.status: Makefile.iptrules.in
28 |
29 | tmpdir := $(shell mktemp -dtu)
30 | packer = gzip
31 | packext = .tar.gz
32 |
33 | GIT_VERSION = $(shell if [ -d .git ]; then git describe; fi)
34 |
35 | testver:
36 | @echo Detected GIT_VERSION:\"${GIT_VERSION}\"
37 |
38 | .PHONY: tarball
39 |
40 | #
41 | # The tarball target is to be used together with the Git repository
42 | # and uses the git "version" provided by 'git describe'
43 | #
44 | tarball:
45 | # do not use mkdir_p here.
46 | @if [ -z "${GIT_VERSION}" ]; then \
47 | echo " *** ERROR: Sorry target tarball require the git tree"; \
48 | exit 42;\
49 | else \
50 | echo " -- Creating tarball GIT version: ${GIT_VERSION}"; \
51 | fi
52 | @mkdir ${tmpdir}
53 | pushd ${top_srcdir} && git archive --prefix=${PACKAGE_NAME}-${GIT_VERSION}/ HEAD | tar -C ${tmpdir} -x && popd;
54 | pushd ${tmpdir}/${PACKAGE_NAME}-${GIT_VERSION} && ./autogen.sh && popd;
55 | tar --use=${packer} -C ${tmpdir} -cf ${PACKAGE_NAME}-${GIT_VERSION}${packext} --owner=root --group=root ${PACKAGE_NAME}-${GIT_VERSION}/;
56 | @rm -Rf ${tmpdir};
57 | @echo " -- Tarball ${PACKAGE_NAME}-${GIT_VERSION}${packext} ready"
58 |
--------------------------------------------------------------------------------
/doc/labsetup.wiki:
--------------------------------------------------------------------------------
1 |
2 | = Lab setup =
3 |
4 | Its possible to setup a simple IPTV testlab using Open Source
5 | components. And you can even introduce and control the packets drops.
6 |
7 | Here are some useful commands to make a testlab setup, where we can
8 | generate a multicast signal (MPEG2 Transport Stream) signal and
9 | control the loss rate.
10 |
11 |
12 | == Create a mpeg2 ts packet multicast stream ==
13 |
14 | Using VLC to create a multicast stream from a file:
15 |
16 | vlc ~/dvdrip/Far_Til_Fire.ts -I rc --sout \
17 | '#duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}'
18 |
19 |
20 | Recode a stream from a file:
21 |
22 | vlc ~/Xx27RyOnruU.flv --ttl 5 -I rc --sout \
23 | '#transcode{vcodec=mp4v,vb=1024,scale=1,acodec=mpga,ab=192,channels=2}:duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}'
24 |
25 |
26 | Send a incoming multicast stream out again using another multicast
27 | address:
28 |
29 |
30 | vlc --ttl 10 udp/ts://@239.123.173.3:5500 --sout \
31 | '#duplicate{dst=std{access=udp,mux=ts,dst=239.254.1.1:5500}}' -I rc
32 |
33 |
34 |
35 | == Dropping packets on purpose ==
36 |
37 | Create a 0.1 percent packet loss on eth6. Notice, we also need to
38 | introduce a delay, so the packet drop/loss have packets to "select"
39 | from.
40 |
41 | tc qdisc add dev eth6 root netem delay 20 ms loss 0.1%
42 |
43 |
44 | Next, change the packet loss percent on eth6 to 1.1 percent, and
45 | introduce a "burst" of loss
46 |
47 | tc qdisc change dev eth6 root netem loss 1.1% 25%
48 |
49 | Think it means give it a 25 percent chance that the next packet drop
50 | occur close to this one. (see explanation: [1] section: "Packet loss").
51 | With their formulation, this will cause 1.1% of packets to be lost,
52 | and each successive probability depends by a quarter on the last one.
53 |
54 | [1] http://www.linuxfoundation.org/collaborate/workgroups/networking/netem
55 |
--------------------------------------------------------------------------------
/iptables-module/compat_skbuff.h:
--------------------------------------------------------------------------------
1 | #ifndef COMPAT_SKBUFF_H
2 | #define COMPAT_SKBUFF_H 1
3 |
4 | struct tcphdr;
5 | struct udphdr;
6 |
7 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 30)
8 | static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst)
9 | {
10 | skb->dst = dst;
11 | }
12 |
13 | static inline struct dst_entry *skb_dst(const struct sk_buff *skb)
14 | {
15 | return skb->dst;
16 | }
17 |
18 | static inline struct rtable *skb_rtable(const struct sk_buff *skb)
19 | {
20 | return (void *)skb->dst;
21 | }
22 | #endif
23 |
24 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19)
25 | # define skb_ifindex(skb) \
26 | (((skb)->input_dev != NULL) ? (skb)->input_dev->ifindex : 0)
27 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->nfmark)
28 | #elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
29 | # define skb_ifindex(skb) (skb)->iif
30 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->mark)
31 | #else
32 | # define skb_ifindex(skb) (skb)->skb_iif
33 | # define skb_nfmark(skb) (((struct sk_buff *)(skb))->mark)
34 | #endif
35 |
36 | #ifdef CONFIG_NETWORK_SECMARK
37 | # define skb_secmark(skb) ((skb)->secmark)
38 | #else
39 | # define skb_secmark(skb) 0
40 | #endif
41 |
42 | #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 21)
43 | # define ip_hdr(skb) ((skb)->nh.iph)
44 | # define ip_hdrlen(skb) (ip_hdr(skb)->ihl * 4)
45 | # define ipv6_hdr(skb) ((skb)->nh.ipv6h)
46 | # define skb_network_header(skb) ((skb)->nh.raw)
47 | # define skb_transport_header(skb) ((skb)->h.raw)
48 | static inline void skb_reset_network_header(struct sk_buff *skb)
49 | {
50 | skb->nh.raw = skb->data;
51 | }
52 | static inline struct tcphdr *tcp_hdr(const struct sk_buff *skb)
53 | {
54 | return (void *)skb_transport_header(skb);
55 | }
56 | static inline struct udphdr *udp_hdr(const struct sk_buff *skb)
57 | {
58 | return (void *)skb_transport_header(skb);
59 | }
60 | #endif
61 |
62 | #endif /* COMPAT_SKBUFF_H */
63 |
--------------------------------------------------------------------------------
/database/database-upgrade-0.9.0-to-0.9.1.sql:
--------------------------------------------------------------------------------
1 | --
2 | -- Upgrading Database scheme
3 | -- From version: 0.9.0
4 | -- To version: 0.9.1
5 | --
6 |
7 | DROP TABLE IF EXISTS event_type;
8 | CREATE TABLE event_type (
9 | bitmask int(10) unsigned NOT NULL default '0',
10 | label varchar(15) NOT NULL,
11 | description varchar(255),
12 | PRIMARY KEY (bitmask)
13 | ) ENGINE=InnoDB;
14 |
15 | LOCK TABLES event_type WRITE;
16 | INSERT INTO event_type (bitmask, label, description) VALUES
17 | ( 1, "new_stream", "New stream detected"),
18 | ( 2, "drop" , "Drops detected, both skips and discon"),
19 | ( 4, "no_signal" , "Stream have stopped transmitting data"),
20 | ( 32, "transition", "The event_state changed since last poll"),
21 | ( 64, "heartbeat" , "Heartbeat event to monitor status"),
22 | (128, "invalid" , "Some invalid event situation arose");
23 | UNLOCK TABLES;
24 |
25 | ALTER TABLE log_event
26 | MODIFY event_type int(10) unsigned NOT NULL default '0';
27 |
28 | ALTER TABLE log_event
29 | MODIFY delta_poll float unsigned default NULL;
30 |
31 | ALTER TABLE log_event
32 | ADD last_update timestamp NOT NULL default '0000-00-00 00:00:00',
33 | ADD delta_update float unsigned default NULL;
34 |
35 | -- Add delta colums for payload_bytes and packets
36 |
37 | ALTER TABLE log_event
38 | ADD `delta_payload_bytes` int(10) unsigned NOT NULL default '0' AFTER packets,
39 | ADD `delta_packets` int(10) unsigned NOT NULL default '0' AFTER packets;
40 |
41 | -- Rename column "bytes" to "payload_bytes"
42 |
43 | ALTER TABLE log_event
44 | CHANGE `bytes` `payload_bytes` bigint(20) unsigned default '0';
45 |
46 | -- Fix the last renaming from mp2t to mpeg2ts
47 |
48 | ALTER TABLE daemon_session
49 | CHANGE `mp2t_created` `mpeg2ts_created` timestamp NOT NULL default '0000-00-00 00:00:00',
50 | CHANGE `mp2t_version` `mpeg2ts_version` varchar(50) default NULL;
51 |
--------------------------------------------------------------------------------
/release.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | #
3 | # Script for making releases, where you are forced to create a git tag
4 | # before you can make a release. If you want tarballs without using a
5 | # git tag, then use the make tarball system.
6 | #
7 | set -e
8 | NAME=iptv-analyzer
9 | VERSION=0.9.4
10 | PREV_VERSION=0.9.3
11 | GPGKEY="3B37BCED"
12 |
13 | echo "Creating tarball for release: $NAME-$VERSION"
14 | echo "============================="
15 |
16 | # Create a unique tempdir, to avoid leftovers from older release builds
17 | TMPDIR=`mktemp -dt $NAME.XXXXXXXXXX`
18 | trap 'rm -rf $TMPDIR' EXIT
19 | #echo TMPDIR:$TMPDIR
20 | PKGDIR="$TMPDIR/${NAME}-${VERSION}"
21 | #echo PKGDIR:$PKGDIR
22 | RELDIR=release
23 | if [ ! -d $RELDIR ]; then
24 | mkdir -p $RELDIR
25 | fi
26 | VERSION_TAG="v${VERSION}"
27 | #VERSION_TAG=HEAD #HACK for testing
28 |
29 | # Compression packer tool
30 | packer=gzip
31 | packext=gz
32 |
33 | #PATCH="$RELDIR/patch-$NAME-$PREV_VERSION-$VERSION.$packext";
34 | TARBALL="$RELDIR/$NAME-$VERSION.tar.$packext";
35 | CHANGES="$RELDIR/changes-$NAME-$PREV_VERSION-$VERSION.txt";
36 |
37 | #mkdir -p "$TMPDIR"
38 | echo " -- Git shortlog v$PREV_VERSION..$VERSION_TAG"
39 | git shortlog "v$PREV_VERSION..$VERSION_TAG" > "$CHANGES"
40 | #git diff "v$PREV_VERSION..$VERSION_TAG" | $packer > "$PATCH"
41 | echo " -- Git archiving version tag $VERSION_TAG"
42 | git archive --prefix="$NAME-$VERSION/" "$VERSION_TAG" | tar -xC "$TMPDIR/"
43 |
44 | pushd "$PKGDIR" > /dev/null && {
45 | echo " -- Generating configure scripts..."
46 | sh autogen.sh
47 | popd > /dev/null
48 | }
49 |
50 | echo " -- Creating tarball $TARBALL"
51 | tar --use=${packer} -C "$TMPDIR" -cf "$TARBALL" "$NAME-$VERSION";
52 |
53 | echo " -- Calculating checksums"
54 | md5sum "$TARBALL" >"${TARBALL}.md5sum";
55 | sha1sum "$TARBALL" >"${TARBALL}.sha1sum";
56 |
57 | echo " -- You need to sign the tarball"
58 | gpg -u "$GPGKEY" -sb "$TARBALL";
59 |
60 | #gpg -u "$GPGKEY" -sb "$PATCH";
61 | #md5sum "$PATCH" >"${PATCH}.md5sum";
62 | #sha1sum "$PATCH" >"${PATCH}.sha1sum";
63 |
--------------------------------------------------------------------------------
/iptables-module/reload_module.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Quick script for reloading the kernel module, from the current
4 | # directory.
5 |
6 | MODULE=xt_mpeg2ts
7 |
8 | echo "Reloading kernel module:"
9 |
10 | # Test if a module is loaded
11 | module_loaded() {
12 | module=$1
13 | echo " -- Test if module:$module is loaded"
14 | egrep -q ^${module} /proc/modules 2> /dev/null
15 | res=$?
16 | return $res
17 | }
18 |
19 | module_rmmod() {
20 | module=$1
21 | echo " -- Unloading module: $module"
22 | sudo rmmod $module
23 | res=$?
24 | return $res
25 | }
26 |
27 | module_insmod() {
28 | module=$1
29 | lmodule=${module}.ko
30 | echo " -- Loading module: ${lmodule}"
31 | if [ -e ${lmodule} ]; then
32 | sudo insmod ./${lmodule}
33 | else
34 | echo "ERROR: cannot find module file: ${lmodule}"
35 | fi
36 | res=$?
37 | return $res
38 | }
39 |
40 | iptables_flush() {
41 | echo " -- Flushing iptables rules"
42 | sudo iptables -F
43 | }
44 |
45 | iptables_create_rules() {
46 | echo " -- Creating iptables rules using mpeg2ts"
47 | sudo iptables -I INPUT -p udp -m mpeg2ts --name input
48 | }
49 |
50 | echo " -- Check if I need to load compat_xtables"
51 | compat=compat_xtables
52 | if (module_loaded $compat); then
53 | echo " ---- OK, compat_xtables already loaded"
54 | else
55 | echo " ---- Loading compat_xtables"
56 | sudo insmod ./${compat}.ko
57 | fi
58 |
59 | if (module_rmmod $MODULE); then
60 | echo " ---- Successfully removed module"
61 | if (! module_insmod $MODULE); then
62 | echo "ERROR(2): Cannot load module, FAILING!"
63 | exit 2
64 | fi
65 | else
66 | echo " ---- Cannot remove module"
67 | echo " ---- Assuming iptables rules have references"
68 | iptables_flush
69 | # Try again
70 | if (module_rmmod $MODULE); then
71 | echo " ---- Successfully removed module"
72 | if (! module_insmod $MODULE); then
73 | echo "ERROR(3): Cannot load module, FAILING!"
74 | exit 3
75 | fi
76 | else
77 | echo "ERROR(4): Still cannot remove module, FAILING!"
78 | exit 4
79 | fi
80 | fi
81 |
82 | iptables_create_rules
83 |
--------------------------------------------------------------------------------
/webfrontend/www/js/epoch_v202_en/minimum_code.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 | Minimum Required Code - Epoch Prime
7 |
8 |
9 |
20 |
21 |
22 |
Minimum Required Code
23 |
This page shows the minimum amount of code required to use Epoch Prime in each mode. Right-click anywhere in the page and select "View Source" to see.
24 |
Internet Explorer Users on Windows XP with Service Pack 2: If you don't see the calendar when you load the page, make sure to click "Allow Blocked Content"—you'll only see it if you're viewing this file on your computer's file system.
\n";
222 |
223 | form_remove_channels($data);
224 |
225 | ?>
226 |
--------------------------------------------------------------------------------
/iptables-module/libxt_mpeg2ts.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Userspace interface for MPEG2 TS match extension "mpeg2ts" for Xtables.
3 | *
4 | * Copyright (c) Jesper Dangaard Brouer , 2009-2013
5 | *
6 | * This program is free software; you can redistribute it and/or
7 | * modify it under the terms of the GNU General Public License; either
8 | * version 2 of the License, or any later version, as published by the
9 | * Free Software Foundation. See .
10 | *
11 | */
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #include
22 | #include "xt_mpeg2ts.h"
23 |
24 | /*
25 | * Userspace iptables/xtables interface for mpeg2ts module.
26 | */
27 |
28 | /* FIXME: don't think this compat check does not cover all versions */
29 | #ifndef XTABLES_VERSION
30 | #define xtables_error exit_error
31 | #endif
32 |
33 | /*
34 | The default match criteria, since version 0.9.0, is to match on
35 | correct MPEG2 TS packets. Previously (<= 0.8.0) a rule would only
36 | match when a drop were detected.
37 |
38 | Its still possible to match on drop detection, via the parameter
39 | "--match-drop" (which is default off).
40 |
41 | Stats on packet drops are available via the proc filesystem. These
42 | drop detection stats can be disabled via inverting the parameter
43 | --drop-detect, eg. "! --drop-detect".
44 |
45 | */
46 | static const struct option mpeg2ts_mt_opts[] = {
47 | {.name = "name", .has_arg = true, .val = 'n'},
48 | {.name = "drop-detect", .has_arg = false, .val = 'd'},
49 | {.name = "match-drop", .has_arg = false, .val = 'm'},
50 | {.name = "max-streams", .has_arg = true, .val = 'x'},
51 | {NULL},
52 | };
53 |
54 | static void mpeg2ts_mt_help(void)
55 | {
56 | printf(
57 | "mpeg2ts (MPEG2 Transport Stream) match options:\n"
58 | "VERSION %s\n"
59 | " [--name ] Name for proc file /proc/net/xt_mpeg2ts/rule_NAME\n"
60 | " [--match-drop] Match on lost TS frames (default: off)\n"
61 | " [--drop-detect] Detect TS frame loss and store stats (default: ON)\n"
62 | " [--max-streams ] Track 'max' number of streams (per rule)\n",
63 | version
64 | );
65 | }
66 |
67 | static void mpeg2ts_mt_init(struct xt_entry_match *match)
68 | {
69 | struct xt_mpeg2ts_mtinfo *info = (void *)match->data;
70 | /* Enable drop detection per default */
71 | info->flags = XT_MPEG2TS_DETECT_DROP;
72 |
73 | /* Match on drops is disabled per default */
74 | /* XT_MPEG2TS_MATCH_DROP */
75 | }
76 |
77 | static int mpeg2ts_mt_parse(int c, char **argv, int invert, unsigned int *flags,
78 | const void *entry, struct xt_entry_match **match)
79 | {
80 | struct xt_mpeg2ts_mtinfo *info = (void *)(*match)->data;
81 | uint32_t num;
82 |
83 | switch (c) {
84 | case 'n': /* --name */
85 | xtables_param_act(XTF_ONLY_ONCE, "mpeg2ts", "--name",
86 | *flags & XT_MPEG2TS_PARAM_NAME);
87 | if (invert)
88 | xtables_error(PARAMETER_PROBLEM, "Inverting name?");
89 | if (strlen(optarg) == 0)
90 | xtables_error(PARAMETER_PROBLEM, "Zero-length name?");
91 | if (strchr(optarg, '"') != NULL)
92 | xtables_error(PARAMETER_PROBLEM,
93 | "Illegal character in name (\")!");
94 | strncpy(info->rule_name, optarg, sizeof(info->rule_name));
95 | info->flags |= XT_MPEG2TS_PARAM_NAME;
96 | *flags |= XT_MPEG2TS_PARAM_NAME;
97 | break;
98 |
99 | case 'd': /* --drop-detect */
100 | if (*flags & XT_MPEG2TS_DETECT_DROP)
101 | xtables_error(PARAMETER_PROBLEM,
102 | "Can't specify --drop-detect option twice");
103 | *flags |= XT_MPEG2TS_DETECT_DROP;
104 |
105 | if (invert)
106 | info->flags &= ~XT_MPEG2TS_DETECT_DROP;
107 | else
108 | info->flags |= XT_MPEG2TS_DETECT_DROP;
109 |
110 | break;
111 |
112 | case 'm': /* --match-drop */
113 | if (*flags & XT_MPEG2TS_MATCH_DROP)
114 | xtables_error(PARAMETER_PROBLEM,
115 | "Can't specify --match-drop option twice");
116 | *flags |= XT_MPEG2TS_MATCH_DROP;
117 |
118 | if (invert)
119 | info->flags &= ~XT_MPEG2TS_MATCH_DROP;
120 | else
121 | info->flags |= XT_MPEG2TS_MATCH_DROP;
122 |
123 | break;
124 |
125 | case 'x': /* --max-streams */
126 | if (*flags & XT_MPEG2TS_MAX_STREAMS)
127 | xtables_error(PARAMETER_PROBLEM,
128 | "Can't specify --max-streams option twice");
129 | *flags |= XT_MPEG2TS_MAX_STREAMS;
130 |
131 | if (invert) {
132 | info->cfg.max = 0;
133 | /* printf("inverted\n"); */
134 | break;
135 | }
136 |
137 | /* OLD iptables style
138 | if (string_to_number(optarg, 0, 0xffffffff, &num) == -1)
139 | xtables_error(PARAMETER_PROBLEM,
140 | "bad --max-stream: `%s'", optarg);
141 | */
142 |
143 | /* C-style
144 | char *end;
145 | num = strtoul(optarg, &end, 0);
146 | */
147 |
148 | /* New xtables style */
149 | if (!xtables_strtoui(optarg, NULL, &num, 0, UINT32_MAX))
150 | xtables_error(PARAMETER_PROBLEM,
151 | "bad --max-streams: `%s'", optarg);
152 |
153 | /* DEBUG: printf("--max-stream=%lu\n", num); */
154 | info->flags |= XT_MPEG2TS_MAX_STREAMS;
155 | info->cfg.max = num;
156 |
157 | break;
158 |
159 | default:
160 | return false;
161 | }
162 |
163 | return true;
164 | }
165 |
166 | static void mpeg2ts_mt_print(const void *entry,
167 | const struct xt_entry_match *match, int numeric)
168 | {
169 | const struct xt_mpeg2ts_mtinfo *info = (const void *)(match->data);
170 |
171 | /* Always indicate this is a mpeg2ts match rule */
172 | printf("mpeg2ts match");
173 |
174 | if (info->flags & XT_MPEG2TS_PARAM_NAME)
175 | printf(" name:\"%s\"", info->rule_name);
176 |
177 | if (!(info->flags & XT_MPEG2TS_DETECT_DROP))
178 | printf(" !drop-detect");
179 |
180 | if ((info->flags & XT_MPEG2TS_MATCH_DROP))
181 | printf(" match-drop");
182 |
183 | if (info->flags & XT_MPEG2TS_MAX_STREAMS)
184 | printf(" max-streams:%u", info->cfg.max);
185 | }
186 |
187 | static void mpeg2ts_mt_save(const void *entry,
188 | const struct xt_entry_match *match)
189 | {
190 | const struct xt_mpeg2ts_mtinfo *info = (const void *)(match->data);
191 |
192 | /* We need to handle --name, --drop-detect, and --max-streams. */
193 | if (info->flags & XT_MPEG2TS_PARAM_NAME)
194 | printf(" --name \"%s\"", info->rule_name);
195 |
196 | if (!(info->flags & XT_MPEG2TS_DETECT_DROP))
197 | printf(" ! --drop-detect");
198 |
199 | if (info->flags & XT_MPEG2TS_MATCH_DROP)
200 | printf(" --match-drop");
201 |
202 | if (info->flags & XT_MPEG2TS_MAX_STREAMS)
203 | printf(" --max-streams %u", info->cfg.max);
204 |
205 | }
206 |
207 | static struct xtables_match mpeg2ts_mt_reg = {
208 | .version = XTABLES_VERSION,
209 | .name = "mpeg2ts",
210 | .revision = 0,
211 | .family = PF_UNSPEC,
212 | .size = XT_ALIGN(sizeof(struct xt_mpeg2ts_mtinfo)),
213 | .userspacesize = offsetof(struct xt_mpeg2ts_mtinfo, hinfo),
214 | .init = mpeg2ts_mt_init,
215 | .help = mpeg2ts_mt_help,
216 | .parse = mpeg2ts_mt_parse,
217 | /* .final_check = mpeg2ts_mt_check,*/
218 | .print = mpeg2ts_mt_print,
219 | .save = mpeg2ts_mt_save,
220 | .extra_opts = mpeg2ts_mt_opts,
221 | };
222 |
223 | static void _init(void)
224 | {
225 | xtables_register_match(&mpeg2ts_mt_reg);
226 | }
227 |
--------------------------------------------------------------------------------
/webfrontend/www/graphs.inc.php:
--------------------------------------------------------------------------------
1 | setBarWidth($res, "%")
18 | #
19 | function calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records=NULL)
20 | {
21 | $min_bucketsz = 10;
22 | $percent = 1.0;
23 |
24 | # Calc based on the number of data records
25 | if (is_numeric($records) && $records > 0) {
26 | $percent = 100 / $records;
27 | }
28 |
29 | # Calc based on the timeperiod and bucket size
30 | if (is_numeric($bucketsz) && $bucketsz > 0) {
31 | $period = $tstampT - $tstampF;
32 | if ($period > 0) {
33 | if ($bucketsz < $min_bucketsz)
34 | $bucketsz = $min_bucketsz;
35 | // Calc the max possible date records
36 | $max_records = $period / $bucketsz;
37 | if ($max_records > 0 && $max_records > $records) {
38 | // This is often the case
39 | $percent = 100 / $max_records;
40 | }
41 | }
42 | }
43 |
44 | return $percent;
45 | }
46 |
47 | function setBarWidth(& $Plot, $bucketsz, $tstampF, $tstampT, $records=NULL)
48 | {
49 | $percent = calcBarWidthPercentage($bucketsz, $tstampF, $tstampT, $records);
50 | $Plot->setBarWidth($percent,"%");
51 | }
52 |
53 | ######
54 | # Generate graph names
55 |
56 | function generateFilename($prefix, & $input, $suffix = 'png')
57 | {
58 | $str_parts=array();
59 |
60 | if (!is_array($input)) {
61 | $err = "Cannot generate a filename, \$input must be an array";
62 | trigger_error("$err", E_USER_ERROR);
63 | }
64 |
65 | if(isset($input['probeid'])) {
66 | $str_parts[]= 'probeid'.$input['probeid'];
67 | }
68 | if(isset($input['tstampF'])) {
69 | $str_parts[]= 'tstampF'.$input['tstampF'];
70 | }
71 | if(isset($input['tstampT'])) {
72 | $str_parts[]= 'tstampT'.$input['tstampT'];
73 | }
74 | if(isset($input['bucketsz'])) {
75 | $str_parts[]= 'bucketsz'.$input['bucketsz'];
76 | }
77 | if(isset($input['maxy'])) {
78 | $str_parts[]= 'maxy'.$input['maxy'];
79 | }
80 |
81 | $generated_string = implode('_',$str_parts);
82 | $name = $prefix . "__" . $generated_string .".$suffix";
83 | return $name;
84 | }
85 |
86 | ######
87 | # Creating the graph elements
88 |
89 | function create_graph_usemap01($width=700, $height=160, $fontsize=7)
90 | {
91 | $Canvas =& Image_Canvas::factory('png',
92 | array('width' => $width,
93 | 'height' => $height,
94 | 'usemap' => true));
95 |
96 | // This is how you get the ImageMap object,
97 | // fx. to save map to file (using toHtml())
98 | $Imagemap = $Canvas->getImageMap();
99 |
100 | // Create the graph
101 | //$Graph =& Image_Graph::factory('graph', array(600, 140));
102 | $Graph =& Image_Graph::factory('graph', $Canvas);
103 |
104 | // add a TrueType font
105 | //$myfont = '/usr/share/fonts/truetype/freefont/FreeSans.ttf';
106 | $myfont = '/usr/share/fonts/truetype/freefont/FreeSerif.ttf';
107 |
108 | $Font =& $Graph->addNew('font', $myfont);
109 | //$Font =& $Graph->addNew('font', 'Verdana');
110 | //$Font =& $Graph->addNew('font', 'Helvetica');
111 |
112 | // set the font size
113 | $Font->setSize($fontsize);
114 |
115 | $Graph->setFont($Font);
116 | #return array(&$Graph, &$Font);
117 | return $Graph;
118 | }
119 |
120 | function create_plotarea_with_title01(&$Graph, &$Font, $title)
121 | {
122 | /* How element are connected is a bit hard to understand, please read:
123 | http://pear.veggerby.dk/wiki/
124 | image_graph:getting_started_guide#creating_the_building_blocks
125 | */
126 |
127 | /*
128 | $Graph->add(
129 | Image_Graph::vertical(
130 | Image_Graph::factory('title', array("$title", 10)),
131 | Image_Graph::vertical(
132 | $Plotarea = Image_Graph::factory('plotarea'),
133 | $Legend = Image_Graph::factory('legend'),
134 | 90
135 | ),
136 | 5
137 | )
138 | );
139 | $Legend->setPlotarea($Plotarea);
140 | */
141 |
142 | /* Here we start with the Title and add the Plotarea to the Title,
143 | its might seem a little odd, but it works...
144 | */
145 | $Title = $Graph->addNew('title', array("$title", 10));
146 | $Plotarea = $Title->addNew('plotarea');
147 | $Plotarea->setFont($Font);
148 |
149 | return $Plotarea;
150 | }
151 |
152 |
153 | function create_plotarea_with_title02(&$Graph, &$Font, $title)
154 | {
155 | $Title = $Graph->addNew('title', array("$title", 10));
156 | /* Notice the Axis settings on plotarea */
157 | $Plotarea = $Title->addNew('plotarea', array('axis', 'axis'));
158 | $Plotarea->setFont($Font);
159 | return $Plotarea;
160 | }
161 |
162 |
163 | function data_addPoints01(& $Dataset, & $data, $droptype, $max_y_value, $urldata)
164 | {
165 | //Data comes from: $data = probe_data_query($probeid);
166 |
167 | $cnt=0;
168 | # LOOP: addPoints
169 | foreach ($data as $row) {
170 | $cnt++;
171 |
172 | #$date = $row['datoen'];
173 | $date = $row['timestamp'];
174 | $skips = $row['skips'];
175 | $drops = $row['drops'];
176 | $value = $row["$droptype"];
177 |
178 | $timemin = $row['timemin'];
179 | $timemax = $row['timemax'];
180 | $period = $timemax - $timemin;
181 |
182 | $records = $row['records'];
183 |
184 | # "title" info in html map
185 | $title = "$droptype:$value";
186 | $title .= " hour:" . date("H:i:s", $date);
187 | $title .= " (day:" . date("j.M", $date) . ")";
188 | $title .= " period:{$period}s";
189 |
190 | if (isset($records)) {
191 | $title .= " records:$records";
192 | }
193 |
194 | //$title = "drops:$drops skips:$skips";
195 | //echo "TEST: value:$value $title \n";
196 |
197 | # Color code if data value exceed max allowed data value
198 | $colorid = 'DROPS';
199 | if ($value > $max_y_value) {
200 | $value = $max_y_value;
201 | $title .= " EXCESSIVE";
202 | $colorid = 'EXCESS';
203 | }
204 |
205 | # Create an URL
206 | $bucketsz = $urldata['bucketsz'];
207 | $probeid = $urldata['probeid'];
208 | $channel = $urldata['channel'];
209 |
210 | #echo "bucketsz:$bucketsz ";
211 | #echo "timemin:$timemin ";
212 | #echo "timemax:$timemax ";
213 | # For the link adjust $timemin/max, e.g. sub/add bucketsz period
214 | if (is_numeric($bucketsz)) {
215 | $timemin -= floor($bucketsz / 2);
216 | $timemax += floor($bucketsz / 2);
217 | }
218 | #echo "timemin2:$timemin ";
219 | #echo "timemax2:$timemax \n";
220 |
221 |
222 | # Calc new bucketsz, by saying how many elements I want on the graph
223 | $wanted_elements = 50;
224 | $zoom_period = $timemax - $timemin;
225 | $newsz = floor($zoom_period / $wanted_elements);
226 | if ($newsz < 10) {
227 | $newsz = 10;
228 | }
229 |
230 | $URL = "?tstampF=$timemin&tstampT=$timemax"
231 | . "&probeid=$probeid"
232 | . "&bucketsz=" . $newsz;
233 |
234 | if (isset($channel)) {
235 | $URL .= "&channel=$channel";
236 | }
237 |
238 | # Add value points to the dataset
239 | $Dataset->addPoint($date, $value,
240 | array(
241 | 'id' => "$colorid",
242 | 'url' => "$URL",
243 | 'alt' => $value,
244 | 'target' => '_self',
245 | 'htmltags' => array('title' => $title)
246 | )
247 | );
248 | }
249 | return $cnt;
250 | }
251 |
252 | function data_addPoints_fake(& $Dataset, $bucketsz,
253 | $tstampF, $tstampT, $urldata=NULL)
254 | {
255 | $cnt=0;
256 | # LOOP: addPoints
257 | for ($i = $tstampF; $i < $tstampT; $i = $i + $bucketsz) {
258 | $cnt++;
259 |
260 | $date = $i;
261 | $value = 1;
262 |
263 | # "title" info in html map
264 | $title = "no drops";
265 | $title .= " hour:" . date("H:i:s", $date);
266 | $title .= " (day:" . date("j.M", $date) . ")";
267 |
268 | # Color code
269 | $colorid = 'NODROPS';
270 |
271 | # Create an URL
272 | $bucketsz = $urldata['bucketsz'];
273 | $probeid = $urldata['probeid'];
274 |
275 | $URL = "?tstampF=$tstampF&tstampT=$tstampT"
276 | . "&probeid=$probeid&bucketsz="
277 | . $bucketsz;
278 |
279 | # Add value points to the dataset
280 | $Dataset->addPoint($date, $value,
281 | array(
282 | 'id' => "$colorid",
283 | 'url' => "$URL",
284 | 'alt' => $value,
285 | 'target' => '_self',
286 | 'htmltags' => array('title' => $title)
287 | )
288 | );
289 | }
290 | return $cnt;
291 | }
292 |
293 |
294 |
295 | ?>
--------------------------------------------------------------------------------
/snmp/mibs/IPTV-ANALYZER-MIB.txt:
--------------------------------------------------------------------------------
1 | IPTV-ANALYZER-MIB DEFINITIONS ::= BEGIN
2 |
3 | IMPORTS
4 | cxnet
5 | FROM CXNET-CORE-MIB
6 | MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, IpAddress
7 | FROM SNMPv2-SMI;
8 |
9 |
10 | iptvAnalyzer MODULE-IDENTITY
11 | LAST-UPDATED "201105300000Z"
12 | ORGANIZATION "ComX Networks A/S"
13 | CONTACT-INFO
14 | "email: netoptimizer@brouer.com
15 | Author: Jesper Dangaard Brouer"
16 | DESCRIPTION
17 | "Fixing syntax of the MIB file"
18 | REVISION "201105300000Z"
19 | DESCRIPTION
20 | "The MIB module is part of the IPTV-Analyzer
21 | project, and is used for documenting the
22 | snmptraps used by the iptv-collector."
23 | -- 1.3.6.1.4.1.26124.43
24 | ::= { cxnet 43 }
25 |
26 |
27 | -- The General Group is for global settings of the collector deamon
28 |
29 | -- The variables can be used for identifying the collector probe
30 |
31 | collectorGeneralGroup OBJECT IDENTIFIER ::= { iptvAnalyzer 1 }
32 |
33 | -- collector.conf: variable probe_ip
34 | collectorId OBJECT-TYPE
35 | SYNTAX IpAddress
36 | MAX-ACCESS read-write
37 | STATUS current
38 | DESCRIPTION
39 | "Main identifier of the IPTV-collector daemon,
40 | is an IP-address. This comes from the collector.conf
41 | variable probe_ip"
42 | -- 1.3.6.1.4.1.26124.43.1.1
43 | ::= { collectorGeneralGroup 1 }
44 |
45 | -- collector.conf: variable probe_name
46 | collectorName OBJECT-TYPE
47 | SYNTAX OCTET STRING
48 | MAX-ACCESS read-write
49 | STATUS current
50 | DESCRIPTION
51 | "Textual identifier of the IPTV-collector daemon.
52 | This comes from the collector.conf variable probe_name"
53 | -- 1.3.6.1.4.1.26124.43.1.2
54 | ::= { collectorGeneralGroup 2 }
55 |
56 |
57 | -- Trap section
58 |
59 | iptvAnalyzerTrap OBJECT IDENTIFIER ::= { iptvAnalyzer 2 }
60 | -- 1.3.6.1.4.1.26124.43.2
61 |
62 | -- Trap Support Objects
63 | -- The following are support objects for the traps.
64 |
65 | collectorTrapControl OBJECT IDENTIFIER ::= { iptvAnalyzerTrap 1 }
66 | -- 1.3.6.1.4.1.26124.43.2.1
67 |
68 | -- Sub divide: Event properties
69 | collectorEvent OBJECT IDENTIFIER ::= { collectorTrapControl 1 }
70 | -- 1.3.6.1.4.1.26124.43.2.1.1
71 |
72 | eventType OBJECT-TYPE
73 | -- Notice MIBs do NOT like underscores in definitions, thus the
74 | -- event type naming is converted to camel notation.
75 | SYNTAX INTEGER {
76 | newStream (1), -- New stream detected
77 | drop (2), -- Drops detected, both skips and discon
78 | noSignal (4), -- Stream have stopped transmitting data
79 | -- to clear "noSignal" use eventSeverity = 0
80 | ttlChange (16), -- Indicate TTL changed
81 | transition (32), -- The event_state changed since last poll
82 | heartbeat (64), -- Heartbeat event to monitor status
83 | invalid (128) -- Some invalid event situation arose
84 | }
85 | MAX-ACCESS read-only
86 | STATUS current
87 | DESCRIPTION
88 | "The different Event Types used by the collector."
89 | -- 1.3.6.1.4.1.26124.43.2.1.1.1
90 | ::= { collectorEvent 1 }
91 |
92 | eventName OBJECT-TYPE
93 | SYNTAX OCTET STRING ( SIZE(15) )
94 | MAX-ACCESS read-only
95 | STATUS current
96 | DESCRIPTION
97 | "Textual name representatiog of the Event Type"
98 | -- 1.3.6.1.4.1.26124.43.2.1.1.2
99 | ::= { collectorEvent 2 }
100 |
101 | eventSeverity OBJECT-TYPE
102 | -- severity levels taken from ZenOSS
103 | -- http://community.zenoss.org/docs/DOC-4766#d0e6170
104 | -- Number Name Color in ZenOSS
105 | -- 0 Clear Green
106 | -- 1 Debug Grey
107 | -- 2 Info Blue
108 | -- 3 Warning Yellow
109 | -- 4 Error Orange
110 | -- 5 Critical Red
111 | SYNTAX INTEGER {
112 | clear (0), -- Clears the eventType
113 | debug (1),
114 | info (2),
115 | warning (3),
116 | error (4),
117 | critical (5)
118 | }
119 | MAX-ACCESS read-only
120 | STATUS current
121 | DESCRIPTION
122 | "The severity level of an event. Primarily used for
123 | clearing eventTypes, e.g. clear no_signal event."
124 | -- 1.3.6.1.4.1.26124.43.2.1.1.3
125 | ::= { collectorEvent 3 }
126 |
127 |
128 | -- Future:
129 | -- EventBitVector OBJECT-TYPE
130 | -- Can contain more events in its bitVector
131 |
132 | -- Sub divide: Stream properties
133 | streamEvent OBJECT IDENTIFIER ::= { collectorTrapControl 2 }
134 | -- 1.3.6.1.4.1.26124.43.2.1.2
135 |
136 | multicastDest OBJECT-TYPE
137 | SYNTAX IpAddress
138 | MAX-ACCESS read-only
139 | STATUS current
140 | DESCRIPTION
141 | "The multicast IP address of the mpeg2ts stream.
142 | This can also be a ordenary IP if multicast is not used."
143 | -- 1.3.6.1.4.1.26124.43.2.1.2.1
144 | ::= { streamEvent 1 }
145 |
146 | streamerSource OBJECT-TYPE
147 | SYNTAX IpAddress
148 | MAX-ACCESS read-only
149 | STATUS current
150 | DESCRIPTION
151 | "The multicast IP address of the mpeg2ts stream.
152 | This can also be a ordenary IP if multicast is not used."
153 | -- 1.3.6.1.4.1.26124.43.2.1.2.2
154 | ::= { streamEvent 2 }
155 |
156 | -- Future:
157 | -- streamTimeToLive OBJECT-TYPE
158 | -- streamDstPort OBJECT-TYPE
159 | -- streamSrcPort OBJECT-TYPE
160 | -- channelName OBJECT-TYPE
161 |
162 |
163 | -- Sub divide: Config "input" settings from collector.conf
164 | collectorInput OBJECT IDENTIFIER ::= { collectorTrapControl 3 }
165 | -- 1.3.6.1.4.1.26124.43.2.1.3
166 |
167 | inputKey OBJECT-TYPE
168 | SYNTAX OCTET STRING
169 | MAX-ACCESS read-only
170 | STATUS current
171 | DESCRIPTION
172 | "The input[key] used in collector.conf. This is used for
173 | referencing the mpeg2ts proc file input."
174 | -- 1.3.6.1.4.1.26124.43.2.1.3.1
175 | ::= { collectorInput 1 }
176 |
177 | inputShortloc OBJECT-TYPE
178 | SYNTAX OCTET STRING
179 | MAX-ACCESS read-only
180 | STATUS current
181 | DESCRIPTION
182 | "The input[key][shortloc] used in collector.conf. This is used for
183 | referencing input options."
184 | -- 1.3.6.1.4.1.26124.43.2.1.3.2
185 | ::= { collectorInput 2 }
186 |
187 | inputSwitch OBJECT-TYPE
188 | SYNTAX OCTET STRING
189 | MAX-ACCESS read-only
190 | STATUS current
191 | DESCRIPTION
192 | "The input[key][switch] used in collector.conf. This is used for
193 | referencing input options."
194 | -- 1.3.6.1.4.1.26124.43.2.1.3.3
195 | ::= { collectorInput 3 }
196 |
197 | -- Sub divide: collector information
198 | collectorInfo OBJECT IDENTIFIER ::= { collectorTrapControl 4 }
199 | -- 1.3.6.1.4.1.26124.43.2.1.4
200 |
201 | collectorVersion OBJECT-TYPE
202 | SYNTAX OCTET STRING
203 | MAX-ACCESS read-only
204 | STATUS current
205 | DESCRIPTION
206 | "The version of the IPTV-collector."
207 | -- 1.3.6.1.4.1.26124.43.2.1.4.1
208 | ::= { collectorInfo 1 }
209 |
210 | mpeg2tsVersion OBJECT-TYPE
211 | SYNTAX OCTET STRING
212 | MAX-ACCESS read-only
213 | STATUS current
214 | DESCRIPTION
215 | "The version of the mpeg2ts kernel module."
216 | -- 1.3.6.1.4.1.26124.43.2.1.4.2
217 | ::= { collectorInfo 2 }
218 |
219 | --
220 | -- Traps
221 | -- Defining what objects a given trap contains
222 | --
223 | collectorTraps OBJECT IDENTIFIER ::= { iptvAnalyzerTrap 2 }
224 | -- 1.3.6.1.4.1.26124.43.2.2
225 |
226 | generalEvent NOTIFICATION-TYPE
227 | OBJECTS {
228 | collectorVersion,
229 | collectorId, -- The originator of the trap
230 | collectorName,
231 | eventType, -- The event type
232 | eventName
233 | -- Perhaps use sysLocation from SNMPv2-MIB, and use input[key][location]?
234 | }
235 | STATUS current
236 | DESCRIPTION
237 | "General event"
238 | -- 1.3.6.1.4.1.26124.43.2.2.1
239 | ::= { collectorTraps 1 }
240 |
241 | streamNoSignal NOTIFICATION-TYPE
242 | OBJECTS {
243 | collectorVersion,
244 | collectorId, -- The originator of the trap
245 | collectorName,
246 | eventType, -- Event type: noSignal(4)
247 | eventName, -- "no_signal"
248 | eventSeverity, -- clear(0) indicate signal have returned
249 | multicastDest,
250 | streamerSource,
251 | inputKey, -- The collectors input[key]
252 | inputShortloc,
253 | inputSwitch
254 | -- Perhaps use:
255 | -- sysLocation from SNMPv2-MIB, and use input[key][location]?
256 | -- sysName which is normally FQDN/hostname
257 | }
258 | STATUS current
259 | DESCRIPTION
260 | "This trap should be generated when the collector detects
261 | no_signal for a given stream. The trap is generated both
262 | when the signal disappears (eventType==noSignal(4)) and when
263 | the signal starts transmitting again (eventType==okSignal(8))."
264 | -- 1.3.6.1.4.1.26124.43.2.2.2
265 | ::= { collectorTraps 2 }
266 |
267 | -- Future:
268 | -- detectNewStream NOTIFICATION-TYPE
269 |
270 | END
271 |
--------------------------------------------------------------------------------
/configure.ac:
--------------------------------------------------------------------------------
1 | #
2 | # Notice: The VERSION is defined/controlled by AC_INIT
3 | #
4 | AC_INIT([iptv-analyzer], [0.9.4], [netoptimizer@brouer.com], [], [www.iptv-analyzer.org])
5 | #AC_INIT(package, version, [bug-report], [tarname], [url])
6 | #
7 | AC_CONFIG_HEADERS([config.h])
8 | AC_CONFIG_MACRO_DIR([m4])
9 | #AC_PROG_INSTALL
10 | #AM_INIT_AUTOMAKE([1.10.2 -Wall foreign subdir-objects])
11 | AM_INIT_AUTOMAKE([foreign])
12 | #AC_DISABLE_STATIC
13 | #AC_PROG_LIBTOOL
14 |
15 | # Checks for programs.
16 | AC_PROG_AWK
17 | AC_PROG_CC
18 | AM_PROG_CC_C_O
19 | AC_PROG_CPP
20 | AC_PROG_INSTALL
21 | AC_PROG_LN_S
22 | AC_PROG_MAKE_SET
23 | AC_PROG_RANLIB
24 |
25 | # Checks for header files.
26 | AC_CHECK_HEADERS([netdb.h stddef.h stdlib.h string.h])
27 |
28 | # Checks for typedefs, structures, and compiler characteristics.
29 | AC_HEADER_STDBOOL
30 | AC_C_INLINE
31 | AC_TYPE_INT16_T
32 | AC_TYPE_INT32_T
33 | AC_TYPE_INT8_T
34 | AC_TYPE_SIZE_T
35 | AC_TYPE_UINT16_T
36 | AC_TYPE_UINT32_T
37 | AC_TYPE_UINT64_T
38 | AC_TYPE_UINT8_T
39 |
40 | # Checks for library functions.
41 | AC_CHECK_FUNCS([strchr])
42 |
43 | AC_ARG_WITH([kbuild],
44 | AS_HELP_STRING([--with-kbuild=PATH],
45 | [Path to kernel build directory [[/lib/modules/CURRENT/build]]]),
46 | [kbuilddir="$withval"],
47 | [kbuilddir="/lib/modules/$(uname -r)/build"])
48 | #
49 | # check for --without-kbuild
50 | #
51 | if [[ "$kbuilddir" == no ]]; then
52 | kbuilddir="";
53 | fi
54 |
55 | # Check iptables binary exist and set variable IPTABLES to the program
56 | # with full path. Add "sbin" dirs to the search PATH as the user
57 | # running configure might not have this in their path.
58 | #
59 | SBIN_PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin:
60 | AC_PATH_PROG([IPTABLES], [iptables], [no], $SBIN_PATH)
61 | if [[ "$IPTABLES" = "no" ]]; then
62 | AC_MSG_ERROR([Cannot find iptables binary - You need to install iptables])
63 | fi
64 | # Extract iptables base dirname / path
65 | IPTABLES_BINDIR=`AS_DIRNAME([$IPTABLES])`
66 | IPTABLES_DIR=`AS_DIRNAME([$IPTABLES_BINDIR])`
67 |
68 | # Find the include path for xtables.h
69 | IPTABLES_INC="${IPTABLES_DIR}/include"
70 | if [[ "$IPTABLES_DIR" = "/" ]]; then
71 | IPTABLES_INC="/usr/include"
72 | fi
73 |
74 | # xtlibdir the Xtables extensions path
75 | AC_ARG_WITH([xtlibdir],
76 | AS_HELP_STRING([--with-xtlibdir=PATH],
77 | [Path where to install Xtables extensions.
78 | Configure will try to auto DETECT, based on iptables binary path]),
79 | [xtlibdir="$withval"],
80 | [xtlibdir="detect"])
81 | # [xtlibdir='${libexecdir}/xtables'])
82 |
83 | # Default setting of xtlibdir is "detect".
84 | # ----------------------------------------
85 | # Try to detect xtlibdir based upon the location of the detected
86 | # iptables binary IPTABLES_DIR, unless --with-xtlibdir is used.
87 | #
88 | # Problem statement:
89 | #
90 | # The userspace lib dir is differs on different Linux distributions,
91 | # further more the distributions choose not to follow the source code
92 | # (and just alter the exec_prefix to libexec). Instead they force
93 | # the usage of /lib/xtables/ or /lib64/xtables/, via
94 | #
95 | # ./configure --with-xtlibdir=/lib/xtables
96 | #
97 | # We also support this configure switch, but if none given, try to
98 | # detect the xtlibdir used by iptables on the system.
99 | #
100 | xtlibdir_check="";
101 | if test "${with_xtlibdir+set}" = set; then
102 | # No detection and expantion needed, if specified via --with-xtlibdir
103 | xtlibdir_check=$xtlibdir;
104 | elif test "${xtlibdir}" = "detect"; then
105 |
106 | AC_MSG_NOTICE([Entering auto-detect mode for Xtables extensions dir xtlibdir])
107 | test_dir=""
108 | if [[ "${IPTABLES_DIR}" = "/" -o "${IPTABLES_DIR}" = "/usr" ]]; then
109 |
110 | # First detect via pkt-config
111 | xtlibdir="$(pkg-config --variable=xtlibdir xtables)"
112 | if [[ -z "$xtlibdir" ]]; then
113 | # Try a more low level detect method
114 | # Need to test for /lib{,64}/xtables/ and /usr/lib{,64}/xtables/
115 | if [[ -d /lib/xtables ]]; then
116 | xtlibdir=/lib/xtables
117 | elif [[ -d /lib64/xtables ]]; then
118 | xtlibdir=/lib64/xtables
119 | elif [[ -d /usr/lib/xtables ]]; then
120 | xtlibdir=/usr/lib/xtables
121 | elif [[ -d /usr/lib64/xtables ]]; then
122 | xtlibdir=/usr/lib64/xtables
123 | else
124 | xtlibdir=""
125 | test_dir=""
126 | fi
127 | fi
128 | if [[ -n "$xtlibdir" ]]; then
129 | AC_MSG_NOTICE([ - Found xtlibdir: $xtlibdir])
130 | test_dir=$xtlibdir
131 | fi
132 | else
133 | test_dir=${IPTABLES_DIR}/libexec/xtables
134 | if [[ -d "$test_dir" ]]; then
135 | AC_MSG_NOTICE([ - Found xtlibdir: $test_dir])
136 | xtlibdir=$test_dir
137 |
138 | # Handle the case where IPTABLES_DIR = $prefix and
139 | # then pass it along with a quoted '$prefix'
140 | myprefix=${prefix}
141 | if [[ "${myprefix}" = "NONE" ]]; then
142 | myprefix=$ac_default_prefix
143 | fi
144 | if [[ "${myprefix}" = "${IPTABLES_DIR}" ]]; then
145 | xtlibdir='${prefix}/libexec/xtables'
146 | fi
147 | # Handle if exec_prefix is set
148 | if [[ "${exec_prefix}" != "NONE" ]]; then
149 | if [[ "${exec_prefix}" = "${IPTABLES_DIR}" ]]; then
150 | xtlibdir='${libexecdir}/xtables'
151 | fi
152 | fi
153 | else
154 | xtlibdir="$(pkg-config --variable=xtlibdir xtables)"
155 | test_dir=$xtlibdir
156 | fi
157 | fi
158 | # Double check the $test_dir
159 | xtlibdir_check=$test_dir
160 | else
161 | # Skip/avoid check if "detect" is not in use/specified
162 | xtlibdir_check="/"
163 | fi
164 | #
165 | # Test directory exist
166 | if [[ ! -d "$xtlibdir_check" ]]; then
167 | AC_MSG_ERROR([Xtables extensions dir ${xtlibdir_check} does not exist], [2])
168 | fi
169 |
170 | # This adds a dependency to pkg-config, alternativly we could define
171 | # libxtables_CFLAGS and libxtables_LIBS to avoid the need to call
172 | # pkg-config.
173 | PKG_CHECK_MODULES([libxtables], [xtables >= 1.4.3])
174 |
175 | AC_CHECK_HEADERS([linux/netfilter/x_tables.h], [],
176 | [AC_MSG_ERROR([You need to have linux/netfilter/x_tables.h, see INSTALL file for details])])
177 |
178 | regular_CFLAGS="-D_LARGEFILE_SOURCE=1 -D_LARGE_FILES -D_FILE_OFFSET_BITS=64 \
179 | -D_REENTRANT -Wall -Waggregate-return -Wmissing-declarations \
180 | -Wmissing-prototypes -Wredundant-decls -Wshadow -Wstrict-prototypes \
181 | -Winline -pipe -DXTABLES_LIBDIR=\\\"\${xtlibdir}\\\" \
182 | ";
183 |
184 | #
185 | # Check kernel version
186 | #
187 | if grep -q "CentOS release 5\." /etc/redhat-release 2>/dev/null ||
188 | grep -q "Red Hat Enterprise Linux Server release 5" /etc/redhat-release 2>/dev/null; then
189 | #
190 | # Well, just a warning. Maybe the admin updated the kernel.
191 | echo "WARNING: This distribution's shipped kernel is not supported.";
192 | fi;
193 | krel="$(make -sC ${kbuilddir} kernelrelease)";
194 | krel="${krel%%-*}";
195 | kmajor="${krel%%.*}";
196 | krel="${krel#*.}";
197 | kminor="${krel%%.*}";
198 | krel="${krel#*.}";
199 | kmicro="${krel%%.*}";
200 | if test "$kmicro" = "$krel"; then
201 | kstable=0;
202 | else
203 | kstable="${krel#*.}";
204 | if test -z "$kstable"; then
205 | kstable=0;
206 | fi;
207 | fi;
208 | echo "Found kernel version $kmajor.$kminor.$kmicro.$kstable in $kbuilddir";
209 | if test "$kmajor" -gt 2 -o "$kminor" -gt 6 -o "$kmicro" -gt 38; then
210 | echo "WARNING: You are trying a newer kernel. Results may vary. :-)";
211 | elif test \( "$kmajor" -lt 2 -o "$kminor" -lt 6 -o "$kmicro" -lt 28 \); then
212 | echo "ERROR: Your kernel version is not supported";
213 | echo " Minimum kernel versions > 2.6.27";
214 | echo " (as we need the RCU locking API).";
215 | exit 1;
216 | fi;
217 |
218 | # Need perl for the iptv-collector daemon
219 | AC_PATH_PROG([PERL], [perl], [no])
220 | if [[ "$PERL" = "no" ]]; then
221 | AC_MSG_ERROR([Cannot find perl - You need to install perl to run collector])
222 | fi
223 |
224 | AC_SUBST([regular_CFLAGS])
225 | AC_SUBST([kbuilddir])
226 | AC_SUBST([xtlibdir])
227 | AC_SUBST([IPTABLES])
228 | AC_SUBST([IPTABLES_DIR])
229 | AC_SUBST([IPTABLES_INC])
230 | AC_CONFIG_FILES([
231 | Makefile
232 | iptables-module/Makefile
233 | collector/lib/IPTV/Analyzer/Version.pm
234 | collector/etc/version
235 | ])
236 |
237 | #
238 | # $prefix expand for perl collector
239 | PERL_PREFIX="";
240 | if [[ "${prefix}" != "NONE" ]]; then
241 | PERL_PREFIX="PREFIX=$prefix"
242 | fi
243 |
244 | AC_OUTPUT
245 |
246 | # Configure the iptv-collector
247 | #
248 | # Which is based upon Perl MakeMaker system. To integrate, call the
249 | # MakeMaker system, from the configure script.
250 | #
251 | # Remember to pass $prefix parameter along
252 | echo " Processing Collector Daemon"
253 | echo " -- Calling Perl MakeMaker system"
254 | cd collector
255 | ${PERL} Makefile.PL $PERL_PREFIX
256 | cd ..
257 |
258 | echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-"
259 | echo " Visit the project homepage at:"
260 | echo " http://$PACKAGE_URL"
261 | echo ""
262 | echo " Create bugreports, patches and insults via:"
263 | echo " https://github.com/netoptimizer/IPTV-Analyzer/issues"
264 | echo " or email:"
265 | echo " $PACKAGE_BUGREPORT"
266 | echo ""
267 | echo "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-"
268 |
--------------------------------------------------------------------------------
/collector/lib/IPTV/Analyzer/snmptrap.pm:
--------------------------------------------------------------------------------
1 | package IPTV::Analyzer::snmptrap;
2 |
3 | =head1 NAME
4 |
5 | IPTV::Analyzer::snmptrap - module for sending SNMP traps events
6 |
7 | =head1 SYNOPSIS
8 |
9 | The IPTV::Analyzer::snmptrap module is a helper module for sending
10 | SNMP traps, when e.g. events like no-signal occurs.
11 |
12 | =cut
13 |
14 | use strict;
15 | use warnings;
16 |
17 | use Net::SNMP qw(:ALL);
18 |
19 | #use Config::File;
20 | use Data::Dumper;
21 |
22 | use Log::Log4perl qw(get_logger :levels);
23 | our $logger = get_logger(__PACKAGE__);
24 |
25 | use IPTV::Analyzer::Config;
26 | use IPTV::Analyzer::Version;
27 |
28 | BEGIN {
29 | use Exporter ();
30 | our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
31 |
32 | # Package version
33 | $VERSION = $IPTV::Analyzer::Version::VERSION;
34 |
35 | @ISA = qw(Exporter);
36 | @EXPORT = qw(
37 | open_snmp_session
38 | close_snmp_session
39 | send_snmptrap
40 | );
41 |
42 | }
43 |
44 | # Global vars
45 | our $snmp_session = undef;
46 |
47 | sub open_snmp_session($$)
48 | {
49 | my $host = shift;
50 | my $community = shift;
51 | my $version = 2;
52 |
53 | ($snmp_session, my $error) =
54 | Net::SNMP->session(
55 | -hostname => $host,
56 | -version => $version,
57 | -community => $community,
58 | -port => SNMP_TRAP_PORT
59 | );
60 |
61 | if (!defined($snmp_session)) {
62 | $logger->fatal("Cannot open a SNMP session: $error");
63 | return 0;
64 | #exit 1
65 | }
66 | return 1;
67 | }
68 |
69 | sub close_snmp_session()
70 | {
71 | if (defined $snmp_session) {
72 | $snmp_session->close();
73 | }
74 | $snmp_session = undef;
75 | }
76 |
77 | # taken from snmptrapd-sendtest.pl / Net-SNMPTrapd-0.04
78 | #our %opt;
79 | #$opt{version} = $opt{version} || 2;
80 | #$opt{community} = $opt{community} || 'public';
81 | #$opt{integer} = $opt{integer} || 1;
82 | #$opt{string} = $opt{string} || 'String';
83 | #$opt{oid} = $opt{oid} || '1.2.3.4.5.6.7.8.9';
84 | #$opt{ip} = $opt{ip} || '10.10.10.1';
85 | #$opt{counter32} = $opt{counter32} || 32323232;
86 | #$opt{gauge32} = $opt{gauge32} || 42424242;
87 | #$opt{timeticks} = $opt{timeticks} || time();
88 | #$opt{opaque} = $opt{opaque} || 'opaque data';
89 | #$opt{inform} = $opt{inform} || 0;
90 |
91 |
92 | # Which config file input[] source does this trap concern
93 | sub construct_input_identification($)
94 | {
95 | my $inputKey = shift;
96 | my $cfg = get_config();
97 |
98 | # Specific config for input this trap concerns
99 | my $inputShortloc = $cfg->get_input_value($inputKey, "shortloc");
100 | my $inputSwitch = $cfg->get_input_value($inputKey, "switch");
101 |
102 | my @array = (
103 | # inputKey, -- The collectors input[key]
104 | '1.3.6.1.4.1.26124.43.2.1.3.1', OCTET_STRING, $inputKey,
105 | # inputShortloc input[key][shortloc]
106 | '1.3.6.1.4.1.26124.43.2.1.3.2', OCTET_STRING, $inputShortloc,
107 | # inputSwitch input[key][switch]
108 | '1.3.6.1.4.1.26124.43.2.1.3.3', OCTET_STRING, $inputSwitch
109 | );
110 | return @array;
111 | }
112 |
113 | sub construct_version()
114 | {
115 | my $version = $IPTV::Analyzer::Version::VERSION || "0.0.0";
116 | my @array = (
117 | # collectorsVersion
118 | '1.3.6.1.4.1.26124.43.2.1.4.1', OCTET_STRING, $version,
119 | );
120 | return @array;
121 | }
122 |
123 |
124 | sub construct_probe_identification()
125 | {
126 | my $cfg = get_config();
127 | my $probe_ip = $cfg->get_probe_ip;
128 | my $probe_name = $cfg->get_probe_name;
129 | my @array = (
130 | # CollectorId
131 | '1.3.6.1.4.1.26124.43.1.1', IPADDRESS, $probe_ip,
132 | # CollectorName
133 | '1.3.6.1.4.1.26124.43.1.2', OCTET_STRING, $probe_name,
134 | );
135 | return @array;
136 | }
137 |
138 | sub construct_trap_oid($$)
139 | {
140 | my $trap_oid = shift;
141 | my $timeticks = shift || 0;
142 |
143 | # The first two variable-bindings fields in the snmpV2-trap are
144 | # specified by SNMPv2 and should be:
145 | # sysUpTime.0 - ('1.3.6.1.2.1.1.3.0', TIMETICKS, $timeticks)
146 | # snmpTrapOID.0 - ('1.3.6.1.6.3.1.1.4.1.0', OBJECT_IDENTIFIER, $oid)
147 |
148 | my @array = (
149 | '1.3.6.1.2.1.1.3.0', TIMETICKS, $timeticks,
150 | '1.3.6.1.6.3.1.1.4.1.0', OBJECT_IDENTIFIER, $trap_oid,
151 | );
152 | return @array;
153 | }
154 |
155 | sub construct_event($$$)
156 | {
157 | my $event_type = shift;
158 | my $event_name = shift;
159 | my $event_severity = shift;
160 | my @array = (
161 | # eventType
162 | '1.3.6.1.4.1.26124.43.2.1.1.1', INTEGER, $event_type,
163 | # eventName
164 | '1.3.6.1.4.1.26124.43.2.1.1.2', OCTET_STRING, $event_name,
165 | # eventSeverity (indicate clear/ok signal)
166 | '1.3.6.1.4.1.26124.43.2.1.1.3', INTEGER, $event_severity,
167 | );
168 | return @array;
169 | }
170 |
171 | sub construct_event_via_name($$)
172 | {
173 | my $event_name = shift;
174 | my $severity_name = shift;
175 | my $event_type = lookup_event($event_name);
176 | my $event_severity = lookup_severity($severity_name);
177 | my @array = construct_event($event_type, $event_name, $event_severity);
178 | return @array;
179 | }
180 |
181 | our $global_trap_oids = {
182 | 'no_signal' => '1.3.6.1.4.1.26124.43.2.2.2', # MIB: streamNoSignal
183 | 'generalEvent' => '1.3.6.1.4.1.26124.43.2.2.1',
184 | 'unknown' => '1.3.6.1.4.1.26124.43',
185 | };
186 |
187 | sub lookup_trap($)
188 | {
189 | my $trapname = shift;
190 | my $oid;
191 | my $result=0;
192 | if (exists $global_trap_oids->{"$trapname"} ) {
193 | $oid = $global_trap_oids->{"$trapname"};
194 | $result = 1;
195 | } else {
196 | $oid = $global_trap_oids->{"unknown"};
197 | }
198 | #return ($oid, $result);
199 | return $oid;
200 | }
201 |
202 | sub send_snmptrap($$$$$$)
203 | {
204 | my $event_name = shift;
205 | my $severity_name = shift;
206 | my $inputkey = shift;
207 | my $timeticks = shift || 0;
208 | my $multicast = shift;
209 | my $src_ip = shift;
210 |
211 | my $cfg = get_config();
212 |
213 | if ( !defined($snmp_session) ) {
214 | $logger->fatal("Cannot send SNMPtrap, no snmp session opened!");
215 | return 0;
216 | }
217 |
218 | # The first two required variable-bindings fields in snmpV2-trap
219 | my $trap = lookup_trap($event_name);
220 | my @trap_oid = construct_trap_oid($trap, $timeticks);
221 |
222 | # The collectornVersion info
223 | my @version = construct_version();
224 |
225 | # The event type
226 | my @event_oids = construct_event_via_name($event_name, $severity_name);
227 |
228 | # General identification of the probe
229 | my @ident_probe = construct_probe_identification();
230 |
231 | # Specific identification of config input[key]
232 | my @ident_input = construct_input_identification($inputkey);
233 |
234 | # TODO: Stream identification
235 |
236 | my @oid_array =
237 | (
238 | @trap_oid,
239 | @version,
240 | @ident_probe,
241 | @event_oids,
242 |
243 | # multicastDest
244 | '1.3.6.1.4.1.26124.43.2.1.2.1', IPADDRESS, $multicast,
245 | # streamerSource
246 | '1.3.6.1.4.1.26124.43.2.1.2.2', IPADDRESS, $src_ip,
247 |
248 | @ident_input
249 | );
250 |
251 |
252 | # Check oid_array for undef's as snmpv2_trap cannot handle these
253 | for my $n (2 .. $#oid_array)
254 | {
255 | if (! defined $oid_array[$n] ) {
256 | my $theoid = $oid_array[$n-2];
257 | my $log="OID $theoid contains undef (SNMP trap will fail)";
258 | $logger->error($log);
259 | }
260 | }
261 |
262 | my $result = $snmp_session->snmpv2_trap(
263 | -varbindlist => \@oid_array
264 | );
265 |
266 | if(!$result) {
267 | #print "snmperror, input oid_array:" . Dumper(\@oid_array) . "\n";
268 | $logger->error("Could not send SNMP trap");
269 | }
270 |
271 | return $result;
272 | }
273 |
274 | # Example of datatypes
275 | # '1.3.6.1.4.1.26124.42.3.3', INTEGER, $opt{integer},
276 | # '1.3.6.1.4.1.26124.42.3.4', OCTET_STRING, $opt{string},
277 | # '1.3.6.1.4.1.26124.42.3.5', OBJECT_IDENTIFIER, $opt{oid},
278 | # '1.3.6.1.4.1.26124.42.3.6', IPADDRESS, $opt{ip},
279 | # '1.3.6.1.4.1.26124.42.3.7', COUNTER32, $opt{counter32},
280 | # '1.3.6.1.4.1.26124.42.3.8', GAUGE32, $opt{gauge32},
281 | # '1.3.6.1.4.1.26124.42.3.9', TIMETICKS, $opt{timeticks},
282 | # '1.3.6.1.4.1.26124.42.3.10', OPAQUE, $opt{opaque}
283 |
284 |
285 | 1;
286 | __END__
287 | # Below is documentation for the module.
288 | # One way of reading it: "perldoc IPTV/Analyzer/mpeg2ts.pm"
289 |
290 | =head1 DESCRIPTION
291 |
292 | The MIB is defined in snmp/mibs/IPTV-ANALYZER-MIB.txt
293 |
294 | =head1 DEPENDENCIES
295 |
296 | This module uses the module L for sending snmptraps.
297 |
298 | =head1 AUTHOR
299 |
300 | Jesper Dangaard Brouer, Enetoptimizer@brouer.com.
301 |
302 | =head1 COPYRIGHT AND LICENSE
303 |
304 | Copyright (C) 2009-2013 by Jesper Dangaard Brouer.
305 |
306 | This file is licensed under the terms of the GNU General Public
307 | License 2.0. or newer. See .
308 |
309 | =cut
310 |
--------------------------------------------------------------------------------
/collector/bin/iptv-collector:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl -w -I../lib/
2 | #
3 | # Perl IPTV-Analyzer collector daemon
4 | #
5 | =pod
6 |
7 | =head1 NAME
8 |
9 | iptv-collector -- IPTV-Analyzer Collector daemon
10 |
11 | =head1 DESCRIPTION
12 |
13 | The IPTV-Analyzer Collector daemon, parse and process data from
14 | the iptables module mpeg2ts, via reading files under:
15 |
16 | /proc/net/xt_mpeg2ts/rule_XXX
17 |
18 | The daemon constantly compares the proc file input, to detect changes
19 | for a given multicast channel, e.g drop detection. Changes are
20 | recorded and stored into a MySQL database.
21 |
22 | =head1 CONFIG
23 |
24 | The config file: /etc/iptv-analyzer/collector.conf
25 |
26 | Sample/documentation of the config format see:
27 |
28 | man IPTV::Analyzer::mpeg2ts
29 |
30 | =cut
31 |
32 | use strict;
33 | use warnings;
34 |
35 | use POSIX qw(setsid);
36 | use sigtrap qw(handler sig_handler normal-signals);
37 |
38 | use Data::Dumper;
39 |
40 | use Proc::Daemon; # libproc-daemon-perl
41 | use Proc::PID::File; # libproc-pid-file-perl
42 |
43 | use File::Basename;
44 | use IPTV::Analyzer::mpeg2ts;
45 | use IPTV::Analyzer::snmptrap;
46 |
47 | # Trick to catch stderr messages from e.g. DB calls into Log4perl
48 | use IPTV::Analyzer::Log4perlTrapper;
49 |
50 | # Logger hack for changing the logging level runtime
51 | use Log::Log4perl qw(get_logger :levels);
52 | our $logger = get_logger("IPTV::Analyzer::mpeg2ts");
53 | #$logger->level($DEBUG);
54 |
55 | # Config options
56 | use IPTV::Analyzer::Config;
57 | our $cfg = get_config();
58 | our $interval = $cfg->{'collector_interval'} || 10;
59 | our $heartbeat_interval_ticks = $cfg->{'collector_heartbeat_ticks'} || 60;
60 | our $snmptraphost = $cfg->{'snmptraphost'} || "87.72.129.222";
61 | our $snmpcommunity = $cfg->{'snmpcommunity'} || "public";
62 |
63 | # State variables
64 | our $ticks = 0;
65 | our $run = 1;
66 |
67 | our $restart = 1;
68 |
69 | # We need to TIE STDERR to log4perl, because the DB can report strange
70 | # things.
71 | #
72 | # http://search.cpan.org/~mschilli/Log-Log4perl-1.26/lib/Log/Log4perl/FAQ.pm#Some_module_prints_messages_to_STDERR._How_can_I_funnel_them_to_Log::Log4perl?
73 | #
74 |
75 |
76 | sub daemon()
77 | {
78 | my $pid;
79 | my $rundir = "/var/run/";
80 | my $pidfile = $rundir . basename($0) . ".pid";
81 | if ( not -w $pidfile) {
82 | my $log = "Cannot write to PID file $pidfile";
83 | $logger->logcroak($log);
84 | }
85 |
86 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!";
87 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!";
88 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!";
89 |
90 | # man fork(2): On success, the PID of the child process is
91 | # returned in the parent, and 0 is returned in the child.
92 | #
93 | defined($pid = fork) or $logger->logcroak("Can't fork: $!");
94 | if ($pid) {
95 | open(PID, "> $pidfile")
96 | or $logger->logcroak("Can't open pidfile $pidfile: $!");
97 | print PID $pid;
98 | close(PID);
99 | exit 0;
100 | }
101 |
102 | setsid() or die "Can't start a new session: $!";
103 | chdir '/' or die "Can't chdir to /: $!";
104 | umask 0 or die "Can't umask: $!";
105 |
106 | open STDIN, '< /dev/null' or die "Can't read /dev/null: $!";
107 | open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!";
108 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!";
109 | }
110 |
111 |
112 | sub daemon2()
113 | {
114 | # Problem: perm to /var/run/$0.pid
115 | my $rundir = "/var/run/";
116 | my $pidfile = $rundir . basename($0) . ".pid";
117 | if ( not -w $pidfile) {
118 | my $log = "Cannot write to PID file $pidfile";
119 | $logger->logcroak($log);
120 | }
121 |
122 | # Daemonize
123 | Proc::Daemon::Init();
124 | # After this line we cannot output to stderr/stdout
125 |
126 | # Check if already running
127 | my $otherpid = Proc::PID::File->running();
128 | if ($otherpid > 0) {
129 | my $log = "Daemon is already running as PID:[$otherpid]";
130 |
131 | if ($restart) {
132 | my $cnt = kill("TERM", $otherpid);
133 | if ($cnt < 1) {
134 | $log .= " - could not kill current exit self!";
135 | $logger->error($log);
136 | exit(2);
137 | }
138 | $log .= " - KILL and restart!";
139 | $logger->warn($log);
140 |
141 | sleep(1);
142 | # Write new PID into pid file
143 | if (Proc::PID::File->running()) {
144 | $logger->error("Tried killing $otherpid but failed! - exit");
145 | exit(3);
146 | }
147 |
148 | } else {
149 | $log .= " - exiting buy buy!";
150 | $logger->warn($log);
151 | exit(1);
152 | }
153 | } else {
154 | $logger->info("Starting IPTV::Analyzer collector daemon2");
155 | }
156 | }
157 |
158 |
159 | sub daemon3()
160 | {
161 | # Problem: perm to /var/run/$0.pid
162 | my $rundir = "/var/run/";
163 | my $pidfile = $rundir . basename($0) . ".pid";
164 | if ( -e $pidfile && (not -w $pidfile)) {
165 | my $log = "Cannot write to PID file $pidfile";
166 | $logger->logcroak($log);
167 | }
168 |
169 | open(STDIN, "+>/dev/null");
170 | open(STDOUT, "+>&STDIN");
171 | # open(STDERR, "+>&STDIN");
172 | # tie *STDERR, "Trapper";
173 |
174 |
175 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!";
176 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!";
177 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!";
178 |
179 |
180 | # Daemonize
181 | # man fork(2): On success, the PID of the child process is
182 | # returned in the parent, and 0 is returned in the child.
183 | #
184 | my $pid;
185 | defined($pid = fork) or $logger->logcroak("Can't fork: $!");
186 | if ($pid) {
187 | exit(0);
188 | }
189 |
190 | setsid() or die "Can't start a new session: $!";
191 | chdir '/' or die "Can't chdir to /: $!";
192 | umask 0 or die "Can't umask: $!";
193 |
194 | # open STDIN, '< /dev/null' or die "Can't read /dev/null: $!";
195 | # open STDOUT, '> /dev/null' or die "Can't write /dev/null: $!";
196 | # open STDERR, '> /dev/null' or die "Can't write /dev/null: $!";
197 |
198 | # Check if already running
199 | my $otherpid = Proc::PID::File->running();
200 | if ($otherpid > 0) {
201 | my $log = "Daemon is already running as PID:[$otherpid]";
202 |
203 | if ($restart) {
204 | my $cnt = kill("TERM", $otherpid);
205 | if ($cnt < 1) {
206 |
207 | # Trying to kill again after a delay
208 | my $delay = 5; # = $interval (?)
209 | my $logtry = "could not kill, trying again in $delay sec";
210 | $logger->error("$log - $logtry");
211 | sleep($delay);
212 | # Test if the process is running before killing it again
213 | # (kill with signal 0 is just a test)
214 | if ($otherpid != $$ && kill(0, $otherpid)) {
215 | $cnt = kill("TERM", $otherpid);
216 | if ($cnt < 1) {
217 | # Giving up
218 | $log .= " - could NOT kill current daemon, exit self!";
219 | $logger->fatal($log);
220 | exit(2);
221 | }
222 | }
223 | }
224 | my $delaydb = 3;
225 | $log .= " - KILL and restart (in $delaydb sec)";
226 | $logger->warn($log);
227 |
228 | # Wait a bit for the other process to finish db sessions
229 | sleep($delaydb);
230 | # Write new PID into pid file
231 | if (Proc::PID::File->running()) {
232 | $logger->fatal("Tried killing pid:$otherpid but failed! - exit");
233 | exit(3);
234 | }
235 |
236 | } else {
237 | $log .= " - exiting buy buy!";
238 | $logger->warn($log);
239 | exit(1);
240 | }
241 | } else {
242 | my $ver = $IPTV::Analyzer::mpeg2ts::VERSION;
243 | $logger->info("Started IPTV::Analyzer collector daemon (child) (version:$ver)");
244 | }
245 | }
246 |
247 |
248 | sub sig_handler {
249 | $logger->info("Going to exit due to kill signal");
250 | heartbeat_update();
251 | close_daemon_sessions();
252 | close_stream_sessions();
253 | close_snmp_session();
254 | $run = 0;
255 | }
256 |
257 | #http://search.cpan.org/~mschilli/Log-Log4perl-1.26/lib/Log/Log4perl/FAQ.pm#My_program_already_uses_warn%28%29_and_die%28%29._How_can_I_switch_to_Log4perl?
258 |
259 | #$SIG{__WARN__} = sub {
260 | # local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1;
261 | ### WARN @_;
262 | #### $logger->warn(@_) if defined @_;
263 | # warn(@_) if defined @_;
264 | #};
265 |
266 | $SIG{__DIE__} = sub {
267 | if($^S) {
268 | # We're in an eval {} and don't want log
269 | # this message but catch it later
270 | return;
271 | }
272 | $Log::Log4perl::caller_depth++;
273 | # LOGDIE @_;
274 | $logger->logdie(@_);
275 | };
276 |
277 |
278 | #tie *STDERR, "Trapper";
279 | #tie *STDERR, "IPTV::Analyzer::mpeg2ts";
280 |
281 | ## Testing outputs
282 | #print "TEST1:stdout\n";
283 | #print STDERR "TEST1:stderr\n";
284 | #warn "TEST1:warn()\n";
285 |
286 | my $ver = $IPTV::Analyzer::mpeg2ts::VERSION;
287 | $logger->info("Starting IPTV::Analyzer collector daemon (parent) (version:$ver)");
288 |
289 |
290 | # Fork of this process
291 | #daemon();
292 | #daemon2();
293 | daemon3();
294 |
295 | ## Testing output after the child process has been forked
296 | #print "TEST2:stdout\n";
297 | #print STDERR "TEST2:stderr\n";
298 | #warn "TEST2:warn()\n";
299 |
300 | # IPTV::Analyzer::mpeg2ts
301 | db_connect();
302 |
303 | open_snmp_session($snmptraphost, $snmpcommunity);
304 |
305 | # The loop
306 | while ($run) {
307 |
308 | # Process all inputs according to the config file
309 | process_inputs();
310 |
311 | sleep($interval);
312 | $ticks++;
313 |
314 | if (($ticks % $heartbeat_interval_ticks) == 0) {
315 | heartbeat_state_active();
316 | heartbeat_update();
317 | } else {
318 | heartbeat_state_clear();
319 | }
320 | }
321 |
322 | $logger->info("Stopping IPTV::Analyzer collector daemon (version:$ver)");
323 |
324 | db_commit();
325 | db_disconnect();
326 | close_snmp_session();
327 | exit;
328 |
329 | __END__
330 | =pod
331 |
332 | =head1 COPYRIGHT AND LICENSE
333 |
334 | Copyright (C) 2009-2011+ by Jesper Dangaard Brouer, ComX Networks A/S.
335 |
336 | This file is licensed under the terms of the GNU General Public
337 | License 2.0. or newer. See .
338 |
339 | =cut
340 |
--------------------------------------------------------------------------------
/webfrontend/www/js/epoch_v202_en/gettingstarted.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 | Getting Started with Epoch
8 |
9 |
10 |
11 |
12 |
59 |
60 |
61 |
62 |
Epoch DHTML JavaScript Calendar – Getting Started
63 |
For those who want to dive in, check out the minimum code required page to see the bare minimum you need to run Epoch.
64 |
Introduction
65 |
Epoch is a fast, low-footprint, high-powered JavaScript Calendar for use on websites which demand high interactivity from their users. Epoch can
66 | be used as a datepicker or inline calendar. Epoch is fully standards-compliant, with minimal allotments for obsolete browsers like IE4/Netscape 4, and commonly-used browsers that deviate from web standards.
67 | As a result, Epoch will work for virtually all users with JavaScript enabled – the dominant browsers out there all support it! More importantly, because of its
68 | standards-compliance, Epoch is future-compatible (confirmed with Internet Explorer 7!) and its use of many of the object-oriented features of JavaScript makes it easier for the developer to maintain
69 | and extend.
70 |
71 |
Thanks for your interest in Epoch!
72 | Nick Baicoianu
73 | Epoch Lead Developer
74 |
75 |
License
76 |
Epoch is free for non-commercial use. If you are using Epoch for a commercial website (e-commerce, corporate site, intranet, etc),
77 | please purchase a license. – you'll get free upgrades, full access to the Epoch
78 | source code, as well as email support in setting up your calendar. We think you'll find it's worth the time you saved implementing Epoch versus other solutions.
79 |
80 |
Who Epoch is For
81 |
Website Owners
82 |
Epoch's ease of deployment and maintenance makes it a prime choice for any website. Hoteliers and events websites will find the holiday and blackout date
83 | features of Epoch incredibly useful in detailing their offerings. Bloggers can use Epoch to navigate their archives with ease.
84 | High-traffic sites will appreciate the low bandwidth footprint (24k for all files - compressed) of Epoch when upgrading their user's experiences.
85 |
Never has it been so simple and inexpensive to upgrade your site than with Epoch.
86 |
87 |
Developers
88 |
Epoch is designed to be as easy for the developer to deploy as it is for the user to interact with it. Integrating Epoch into your web pages doesn't require any hacks or complicated code
89 | because of its object-oriented structure. If you aren't familiar with object-oriented programming in JavaScript (or any other language, for that matter),
90 | don't fret! Using Epoch's features requires no special knowledge outside of basic JavaScript and HTML. Everything you need to get Epoch running on your
91 | web pages is included in this file and the example file.
92 |
93 |
Your Site's Visitors
94 |
Before you add Epoch to your web pages, consider your audience: are they advanced users who use the latest technology, or less-experienced
95 | users who may not be using "modern browsers" (i.e. Internet Explorer 4 and below, Netscape 4 and below, IE for Mac)? You can easily check your users'
96 | browsers if your webserver has a web stats program installed – if you use cPanel you have several options available. If you think your users
97 | are the less-experienced type, we recommend you use Epoch only for non-critical applications or in popup mode—that way if your users' browsers
98 | can't run Epoch—or have switched off JavaScript entirely, they won't be left out in the cold.
99 |
100 |
The Basics
101 |
More information on setting up Epoch is available at the Epoch Setup Page.
102 |
For those who want to dive in, check out the minimum code required page to see the bare minimum you need to run Epoch.
103 |
1. Uploading Epoch
104 |
Before you can do anything, you must upload the Epoch files (via FTP or SCP/SFTP) to a web-accessible directory on your webserver. For example, if you site's root directory is /var/www/mysite/ (UNIX/Linux), or
105 | C:\inetpub\mysite\ (for Windows), you can place them in any subdirectory, i.e. /var/www/mysite/javascript/, or C:\inetpub\mysite\css\.
106 |
2. Linking to the Epoch files
107 |
Before you can begin using Epoch in your page, you need to include the files necessary for Epoch to function and look good. There are two files:
108 | a JavaScript file (epoch_classes.js), and a css file(epoch_styles.css).
109 |
The links to both files should go in the <HEAD> section of any webpage that uses Epoch – make sure they point to the correct subdirectory!
Once you've added the 2 files in the page head, you can start using Epoch! To add an Epoch calendar to you page you will first need to create an
118 | empty HTML element in the page body to physically hold the calendar; <div>, <input>, <td>, <p> are all acceptable elements.
119 | The containing element should be placed at the exact location you want Epoch to appear.
120 |
Syntax
121 |
When you've created the container element, you need to initialize an Epoch calendar for each one. You can do this using a line of code like this:
122 |
calendar1 = new Epoch(name,mode,container,multiselect);
123 |
The Initialization Variables
124 |
125 |
name
126 |
A unique name for the calendar – this will become the base for the calendar's id attribute.
127 |
mode
128 |
The mode the calendar will run in – can be flat or popup
129 |
container
130 |
A JavaScript resource pointing to the HTML element that will hold the calendar. Usually accessed through the JavaScript method document.getElementById('container_id').
131 |
multiselect
132 |
Whether you can select multiple dates – true for yes, false for no.
133 |
134 |
This code must lie inside a <SCRIPT> tag.
135 |
Where to put it
136 |
You have a few options on where to put the Epoch initializations – all will work the same.
137 |
In the Page <HEAD>
138 |
<head>
139 | <title>My Cool Calendar Page</title>
140 | <link rel="stylesheet" type="text/css" href="/path_to_css/epoch_styles.css" /> <!--Epoch's styles-->
141 | <script type="text/javascript" src="/path_to_javascript/epoch_classes.js"></script> <!--Epoch's Code-->
142 | <script type="text/javascript">
143 | var calendar1, calendar2, calendar3; /*must be declared in global scope*/
144 | /*put the calendar initializations in the window's onload() method*/
145 | window.onload = function() {
146 | calendar1 = new Epoch('cal1','flat',document.getElementById('calendar1_container'),false);
147 | calendar2 = new Epoch('cal2','popup',document.getElementById('calendar2_container'),false);
148 | calendar3 = new Epoch('cal3','flat',document.getElementById('calendar3_container'),true);
149 | ...
150 | };
151 | </script>
152 | </head>
153 |
In a separate .js file (using the code in blue above)
Inside the containing HTML element (flat mode only)—not recommended
161 |
<body>
162 | <h1>My cool calendars</h1>
163 | <p>Check out our great events calendars!</p>
164 | <div id="calendar1_container">
165 | <script type="text/javascript">new Epoch('cal1','flat',document.getElementById('calendar1_container'),false);</script>
166 | </div>
167 | <!--No "appropriate" way of associating a calendar with an inline script-->
168 | <input id="calendar2_container" type="text" name="date" />
169 | <div id="calendar3_container">
170 | <script type="text/javascript">new Epoch('cal3','flat',document.getElementById('calendar3_container'),true);</script>
171 | </div>
172 | </body>
173 |
174 |
Summary
175 |
In short, integrating Epoch into your webpage requires only 4 basic steps:
176 |
177 |
Copy epoch_classes.js and epoch_styles.css to your web server's JavaScript and CSS directories.
178 |
Include the Epoch files in your page's <head>—be sure to include the correct path to the files!
179 |
Add containing elements in your page's body where you want Epoch to appear. Each element should have a unique id attribute.
180 |
Initialize a calendar for each containing element.
181 |
182 |
For a demo that shows the minimum required code to use Epoch, click here.
183 |
Tools
184 |
The code in epoch_classes.js is heavily commented to make modification easier – developers for more highly-trafficked sites
185 | should compress their JavaScript and CSS files using MeanFreePath's Free JavaScript and CSS code compactor.
186 | The code compactor reduces your files sizes by up to 50% by removing comments and unecessary whitespace from your files. The results are faster loading times
187 | for your users, and JavaScript code that is extremely difficult for humans to read. We recommend you keep a non-compacted version of epoch_classes.js
188 | for your own reference.
189 |
190 |
Support and Other Resources
191 |
We at MeanFreePath want your development experience with Epoch to go as smoothly as possible. Here are some resources to
192 | help make installation and customization easier: