# ----------------------------------------------------------------------
#    Copyright (c) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007
#    NOVELL (All rights reserved)
#
#    Copyright (c) Christian Boltz 2018
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of version 2 of the GNU General Public
#    License published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, contact Novell, Inc.
# ----------------------------------------------------------------------
NAME=apparmor-parser
all:
COMMONDIR=../common/

include $(COMMONDIR)/Make.rules

DESTDIR=/
APPARMOR_BIN_PREFIX=${DESTDIR}/lib/apparmor
SBINDIR=${DESTDIR}/sbin
USR_SBINDIR=${DESTDIR}/usr/sbin
SYSTEMD_UNIT_DIR=${DESTDIR}/usr/lib/systemd/system
CONFDIR=/etc/apparmor
INSTALL_CONFDIR=${DESTDIR}${CONFDIR}
LOCALEDIR=/usr/share/locale
MANPAGES=apparmor.d.5 apparmor.7 apparmor_parser.8 aa-teardown.8 apparmor_xattrs.7

# Test for bison version
# parse.error added in version 3.0
# default behavior changed in version 3.6
# parse.error=verbose supported from 3.0 so just test on that
# TODO move to autoconf
BISON_MAJOR:=$(shell bison --version |  ${AWK} '/^bison/ { print ($$NF) }'  | ${AWK} -F. '{print $$1 }')
USE_PARSE_ERROR:=$(shell test "${BISON_MAJOR}" -ge 3 && echo true)

YACC	:= bison
YFLAGS	:= -d
ifeq ($(USE_PARSE_ERROR),true)
YFLAGS+=--define=parse.error=verbose
endif
LEX	:= flex
LEXFLAGS = -B -v
ifndef DEBUG
LEXFLAGS += --noyy_top_state
endif

CPPFLAGS += -D_GNU_SOURCE

STDLIB_INCLUDE:="\#include <stdlib.h>"
HAVE_REALLOCARRAY:=$(shell echo $(STDLIB_INCLUDE) | ${CPP} ${CPPFLAGS} - - | grep -q reallocarray && echo true)

WARNINGS = -Wall
CXX_WARNINGS = ${WARNINGS} ${EXTRA_WARNINGS}
CPP_WARNINGS =
ifndef CFLAGS
CFLAGS	= -g -O2 -pipe

ifdef DEBUG
CFLAGS += -pg -D DEBUG
endif
ifdef COVERAGE
CFLAGS = -g -pg -fprofile-arcs -ftest-coverage
endif
endif #CFLAGS

HAVE_FLTO_PARTITION_NONE:=$(shell ${CC} -E -flto-partition=none /dev/null 1>/dev/null 2>&1 && echo true)
ifeq ($(HAVE_FLTO_PARTITION_NONE),true)
	CFLAGS += -flto-partition=none
endif

EXTRA_CXXFLAGS = ${CFLAGS} ${CPPFLAGS} ${CXX_WARNINGS} -std=gnu++0x
EXTRA_CFLAGS = ${EXTRA_CXXFLAGS} ${CPP_WARNINGS}

ifeq ($(HAVE_REALLOCARRAY),true)
EXTRA_CXXCFLAGS+=-DHAVE_REALLOCARRAY=1
EXTRA_CFLAGS+=-DHAVE_REALLOCARRAY=1
endif

ifdef DEBUG
LEXLIB	:= -lfl
endif

# override this on the make command to point to where the immunix.h file is
# (yeah this is lame, but since we are tied to the kernel so tightly...)
#INCLUDEDIR = /usr/src/linux/include
INCLUDEDIR =

ifdef INCLUDEDIR
	CFLAGS += -I$(INCLUDEDIR)
endif

# Internationalization support. Define a package and a LOCALEDIR
EXTRA_CFLAGS+=-DPACKAGE=\"${NAME}\" -DLOCALEDIR=\"${LOCALEDIR}\"

SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \
       parser_main.c parser_misc.c parser_merge.c parser_symtab.c \
       parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \
       parser_alias.c common_optarg.c lib.c network.cc \
       mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \
       af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \
       mqueue.cc io_uring.cc all_rule.cc cond_expr.cc
STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \
       file_cache.h immunix.h lib.h mount.h network.h parser.h \
       parser_include.h parser_version.h policy_cache.h policydb.h \
       profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h \
       common_flags.h bignum.h all_rule.h cond_expr.h

SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h
GENERATED_HDRS = af_names.h generated_af_names.h \
       cap_names.h generated_cap_names.h parser_version.h errnos.h
LIBAA_HDRS = libapparmor_re/apparmor_re.h libapparmor_re/aare_rules.h

TOOLS = apparmor_parser

OBJECTS = $(patsubst %.cc, %.o, $(SRCS:.c=.o))

AAREDIR= libapparmor_re
AAREOBJECT = ${AAREDIR}/libapparmor_re.a
AAREOBJECTS = $(AAREOBJECT)
AARE_LDFLAGS = -static-libgcc -static-libstdc++ -L. $(LDFLAGS)
AALIB = -Wl,-Bstatic -lapparmor -Wl,-Bdynamic -lpthread

ifdef WITH_LIBINTL
	AALIB += -lintl
endif

ifdef USE_SYSTEM
  # Using the system libapparmor so Makefile dependencies can't be used
  LIBAPPARMOR_A =
  INCLUDE_APPARMOR =
  APPARMOR_H =
else
  LIBAPPARMOR_SRC = ../libraries/libapparmor
  LOCAL_LIBAPPARMOR_INCLUDE = $(LIBAPPARMOR_SRC)/include
  LOCAL_LIBAPPARMOR_LDPATH = $(LIBAPPARMOR_SRC)/src/.libs

  LIBAPPARMOR_A = $(LOCAL_LIBAPPARMOR_LDPATH)/libapparmor.a
  INCLUDE_APPARMOR = -I$(LOCAL_LIBAPPARMOR_INCLUDE)
  AARE_LDFLAGS += -L$(LOCAL_LIBAPPARMOR_LDPATH)
  APPARMOR_H = $(LOCAL_LIBAPPARMOR_INCLUDE)/sys/apparmor.h
endif
EXTRA_CFLAGS += $(INCLUDE_APPARMOR)

LEX_C_FILES	= parser_lex.c
YACC_C_FILES	= parser_yacc.c parser_yacc.h

TESTS = tst_regex tst_misc tst_symtab tst_variable tst_lib
TEST_CFLAGS = $(EXTRA_CFLAGS) -DUNIT_TEST -Wno-unused-result
TEST_OBJECTS = $(filter-out \
			parser_lex.o \
			parser_yacc.o \
			common_optarg.o \
			parser_main.o \
			policy_cache.o, ${OBJECTS}) \
               $(AAREOBJECTS)
TEST_LDFLAGS = $(AARE_LDFLAGS)
TEST_LDLIBS = $(AALIB)

ifdef V
  VERBOSE = 1
endif
ifndef VERBOSE
  VERBOSE = 0
endif
ifeq ($(VERBOSE),1)
  BUILD_OUTPUT =
  Q =
else
  BUILD_OUTPUT = > /dev/null 2>&1
  Q = @
endif
export Q VERBOSE BUILD_OUTPUT

HDRS=$(STATIC_HDRS) $(GENERATED_HDRS) parser_yacc.h $(LIBAA_HDRS) $(APPARMOR_H)


po/${NAME}.pot: ${SRCS} ${STATIC_HDRS}
	$(MAKE) -C po ${NAME}.pot NAME=${NAME} SOURCES="${SRCS} ${STATIC_HDRS}"

techdoc.pdf: techdoc.tex
	timestamp=$(shell date --utc "+%Y%m%d%H%M%S%z" -r $< );\
	while pdflatex "\def\fixedpdfdate{$$timestamp}\input $<" ${BUILD_OUTPUT} || exit 1 ; \
		grep -q "Label(s) may have changed" techdoc.log; \
	do :; done

techdoc/index.html: techdoc.pdf
	latex2html -show_section_numbers -split 0 -noinfo -nonavigation -noaddress techdoc.tex ${BUILD_OUTPUT}

techdoc.txt: techdoc/index.html
	w3m -dump $< > $@

# targets arranged this way so that people who don't want full docs can
# pick specific targets they want.
arch: 	$(TOOLS)

manpages:	$(MANPAGES)

htmlmanpages:	$(HTMLMANPAGES)

pdf:	techdoc.pdf

docs:	manpages htmlmanpages
extra_docs: pdf

indep: docs
	$(Q)$(MAKE) -C po all

all:	arch indep

.PHONY: coverage
coverage:
	$(MAKE) clean apparmor_parser COVERAGE=1

ifndef USE_SYSTEM
$(LIBAPPARMOR_A):
	@if [ ! -f $@ ]; then \
		echo "error: $@ is missing. Pick one of these possible solutions:" 1>&2; \
		echo "  1) Build against the in-tree libapparmor by building it first and then trying again. See the top-level README for help." 1>&2; \
		echo "  2) Build against the system libapparmor by adding USE_SYSTEM=1 to your make command." 1>&2;\
		exit 1; \
	fi
endif

apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A)
	$(CXX) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ $(OBJECTS) $(LIBS) \
	      ${LEXLIB}  $(AAREOBJECTS) $(AARE_LDFLAGS) $(AALIB)

parser_yacc.c parser_yacc.h: parser_yacc.y $(STATIC_HDRS) $(DYNAMIC_HDRS)
	$(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y

parser_lex.c: parser_lex.l $(HDRS)
	$(LEX) ${LEXFLAGS} -o$@ $<

parser_lex.o: parser_lex.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_misc.o: parser_misc.c $(HDRS) unit_test.h
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_yacc.o: parser_yacc.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_main.o: parser_main.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_interface.o: parser_interface.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_include.o: parser_include.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_merge.o: parser_merge.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_regex.o: parser_regex.c $(HDRS) unit_test.h
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_symtab.o: parser_symtab.c $(HDRS) unit_test.h
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_variable.o: parser_variable.c $(HDRS) unit_test.h
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_policy.o: parser_policy.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_alias.o: parser_alias.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_common.o: parser_common.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

mount.o: mount.cc mount.h $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

common_optarg.o: common_optarg.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

policy_cache.o: policy_cache.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

lib.o: lib.c $(HDRS) unit_test.h
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

dbus.o: dbus.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

signal.o: signal.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

ptrace.o: ptrace.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

network.o: network.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

default_features.o: default_features.c $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

af_rule.o: af_rule.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

af_unix.o: af_unix.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

profile.o: profile.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

rule.o: rule.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

userns.o: userns.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

mqueue.o: mqueue.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

io_uring.o: io_uring.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

all_rule.o: all_rule.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

cond_expr.o: cond_expr.cc $(HDRS)
	$(CXX) $(EXTRA_CFLAGS) -c -o $@ $<

parser_version.h: Makefile
	@echo \#define PARSER_VERSION \"$(VERSION)\" > .ver
	@mv -f .ver $@

# af_names and capabilities generation has moved to common/Make.rules,
# as well as the filtering that occurs for network protocols that
# apparmor should not mediate.

generated_af_names.h: ../common/list_af_names.sh
	../common/list_af_names.sh > $@

af_names.h: generated_af_names.h base_af_names.h
	@cat base_af_names.h | diff -u - generated_af_names.h | grep -v '^.AF_MAX' | grep '^\+[^+]' ; \
	if [ $$? -eq 1 ] ; then \
		cat base_af_names.h | LC_ALL=C sed -n -e 's/[ \t]\?AF_MAX[ \t]\+[0-9]\+,//g'  -e 's/[ \t]\+\?AF_\([A-Z0-9_]\+\)[ \t]\+\([0-9]\+\),/#ifndef AF_\1\n#  define AF_\1 \2\n#endif\nAA_GEN_NET_ENT("\L\1", \UAF_\1)\n/pg' > $@ ; \
		cat base_af_names.h | LC_ALL=C sed -n -e 's/AF_MAX[ \t]\+\([0-9]\+\),\?.*/\n#define AA_AF_MAX \1\n/p' >> $@ ; \
	else \
		echo "Error: new AF names detected; please update base_af_names.h with values from generated_af_names.h" ; \
		exit 1 ; \
	fi

generated_cap_names.h: /usr/include/linux/capability.h
	../common/list_capabilities.sh | LC_ALL=C sed -n -e "s/[ \\t]\\?CAP_\\([A-Z0-9_]\\+\\)/\{\"\\L\\1\", \\UCAP_\\1, NO_BACKMAP_CAP, CAPFLAG_BASE_FEATURE\},\\n/pg" > $@

cap_names.h: generated_cap_names.h base_cap_names.h
	@LC_ALL=C sed -e 's/\([^,]*,[^,]*,\) CAP_[A-Z0-9_]\+,/\1 NO_BACKMAP_CAP,/g' base_cap_names.h | diff -u - generated_cap_names.h | grep '^\+[^+]' ; \
	if [ $$? -eq 1 ] ; then \
		cp base_cap_names.h $@ ; \
	else \
		echo "Error: new capabilities detected please update base_cap_names.h with values from generated_cap_names.h" ; \
		LC_ALL=C sed -e 's/\([^,]*,[^,]*,\) CAP_[A-Z0-9_]\+,/\1 NO_BACKMAP_CAP,/g' base_cap_names.h | diff -u - generated_cap_names.h ; \
		exit 1; \
	fi

.PHONY: tst_binaries
tst_binaries: $(TESTS)

tst_lib: lib.c parser.h $(filter-out lib.o, ${TEST_OBJECTS})
	$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)
tst_%: parser_%.c parser.h $(filter-out parser_%.o, ${TEST_OBJECTS})
	$(CXX) $(TEST_CFLAGS) -o $@ $< $(filter-out $(<:.c=.o), ${TEST_OBJECTS}) $(TEST_LDFLAGS) $(TEST_LDLIBS)

errnos.h:
	echo '#include <errno.h>' > dump.c
	$(CC) $(CPPFLAGS) -E -dD dump.c | $(AWK) '/^#define E/ { printf "{ \"%s\", %s },\n", $$2, $$2 }' > errnos.h
	rm -f dump.c

.SILENT: check
.PHONY: check
check: check_pod_files tests

.SILENT: tests
tests: apparmor_parser ${TESTS}
	sh -e -c 'for test in ${TESTS} ; do echo "*** running $${test}" && ./$${test}; done'
	$(Q)$(MAKE) -s -C tst tests

$(AAREOBJECT): FORCE
	$(MAKE) -C $(AAREDIR) CFLAGS="$(EXTRA_CXXFLAGS)"

.PHONY: install-redhat
install-redhat: install-systemd

.PHONY: install-suse
install-suse: install-systemd
	install -m 755 -d $(SBINDIR)
	ln -sf service $(SBINDIR)/rcapparmor

.PHONY: install-slackware
install-slackware:
	install -m 755 -d $(APPARMOR_BIN_PREFIX)/install
	install -m 755 frob_slack_rc $(APPARMOR_BIN_PREFIX)/install
	install -m 755 -d $(DESTDIR)/etc/rc.d
	install -m 755 rc.apparmor.$(subst install-,,$(@)) $(DESTDIR)/etc/rc.d/rc.apparmor

.PHONY: install-debian
install-debian:

.PHONY: install-unknown
install-unknown:

INSTALLDEPS=arch

ifndef DISTRO
DISTRO=$(shell if [ -f /etc/slackware-version ] ; then \
	         echo slackware ; \
	       elif [ -f /etc/debian_version ] ; then \
	         echo debian ;\
	       elif which rpm > /dev/null ; then \
	         if [ "$$(rpm --eval '0%{?suse_version}')" != "0" ] ; then \
	             echo suse ;\
	         elif [ "$$(rpm --eval '%{_host_vendor}')" = redhat ] ; then \
	            echo redhat ;\
	         elif [ "$$(rpm --eval '0%{?fedora}')" != "0" ] ; then \
	            echo redhat ;\
	         else \
	            echo unknown ;\
	         fi ;\
	       else \
	          echo unknown ;\
	       fi)
endif

ifdef DISTRO
INSTALLDEPS+=install-$(DISTRO)
endif

.PHONY: install
install:
	$(MAKE) install-indep
	$(MAKE) install-arch

.PHONY: install-arch
install-arch: $(INSTALLDEPS)
	install -m 755 -d $(SBINDIR)
	install -m 755 ${TOOLS} $(SBINDIR)

.PHONY: install-indep
install-indep: indep
	install -m 755 -d $(INSTALL_CONFDIR)
	install -m 644 parser.conf $(INSTALL_CONFDIR)
	install -m 755 -d $(APPARMOR_BIN_PREFIX)
	install -m 755 rc.apparmor.functions $(APPARMOR_BIN_PREFIX)
	install -m 755 profile-load $(APPARMOR_BIN_PREFIX)
	$(MAKE) -C po install NAME=${NAME} DESTDIR=${DESTDIR}
	$(MAKE) install_manpages DESTDIR=${DESTDIR}

.PHONY: install-systemd
install-systemd:
	install -m 755 -d $(SYSTEMD_UNIT_DIR)
	install -m 644 apparmor.service $(SYSTEMD_UNIT_DIR)
	install -m 755 apparmor.systemd $(APPARMOR_BIN_PREFIX)
	install -m 755 -d $(USR_SBINDIR)
	install -m 755 aa-teardown $(USR_SBINDIR)

ifndef VERBOSE
.SILENT: clean
endif
.PHONY: clean
clean: pod_clean
	rm -f core core.* *.o *.s *.a *~ *.gcda *.gcno
	rm -f gmon.out
	rm -f $(TOOLS) $(TESTS)
	rm -f $(LEX_C_FILES)
	rm -f $(YACC_C_FILES)
	rm -f $(NAME)*.tar.gz $(NAME)*.tgz
	rm -f $(GENERATED_HDRS)
	rm -rf techdoc.aux techdoc.out techdoc.log techdoc.pdf techdoc.toc techdoc.txt techdoc/
	$(MAKE) -s -C $(AAREDIR) clean
	$(MAKE) -s -C po clean
	$(MAKE) -s -C tst clean

FORCE:
